You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2018/12/07 15:51:21 UTC

[syncope] branch 2_1_X updated: [SYNCOPE-1409] Avoid double read either before and afterwards

This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch 2_1_X
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/2_1_X by this push:
     new fbda7da  [SYNCOPE-1409] Avoid double read either before and afterwards
fbda7da is described below

commit fbda7da85201d082fb2d86c0ef930646cf73d46c
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Fri Dec 7 16:50:55 2018 +0100

    [SYNCOPE-1409] Avoid double read either before and afterwards
---
 .../org/apache/syncope/core/logic/RealmLogic.java  | 14 ++---
 .../org/apache/syncope/core/logic/TaskLogic.java   | 16 +++++-
 .../api/propagation/PropagationManager.java        | 15 +++--
 .../api/propagation/PropagationReporter.java       |  9 ++-
 .../api/propagation/PropagationTaskCallable.java   |  3 +-
 .../api/propagation/PropagationTaskExecutor.java   | 12 ++--
 ...nTaskCallable.java => PropagationTaskInfo.java} | 25 ++++++--
 .../java/DefaultAnyObjectProvisioningManager.java  | 22 ++++----
 .../java/DefaultGroupProvisioningManager.java      | 27 +++++----
 .../java/DefaultUserProvisioningManager.java       | 38 ++++++-------
 .../AbstractPropagationTaskExecutor.java           | 65 +++++++++++----------
 .../propagation/DefaultPropagationReporter.java    | 13 +++--
 .../DefaultPropagationTaskCallable.java            | 14 ++---
 .../PriorityPropagationTaskExecutor.java           | 35 ++++++------
 .../java/propagation/PropagationManagerImpl.java   | 36 ++++++------
 .../java/pushpull/AbstractPushResultHandler.java   | 53 +++++++++++------
 .../pushpull/DefaultRealmPullResultHandler.java    | 14 ++---
 .../pushpull/DefaultRealmPushResultHandler.java    | 66 ++++++++++++++--------
 .../producer/ConfirmPasswordResetProducer.java     |  7 +--
 .../camel/producer/CreateProducer.java             | 22 +++-----
 .../camel/producer/DeleteProducer.java             | 42 +++++++-------
 .../camel/producer/DeprovisionProducer.java        | 18 +++---
 .../camel/producer/ProvisionProducer.java          | 16 +++---
 .../camel/producer/StatusProducer.java             |  9 ++-
 .../camel/producer/SuspendProducer.java            |  7 +--
 .../camel/producer/UpdateProducer.java             | 23 +++-----
 .../syncope/core/logic/UserRequestLogic.java       |  6 +-
 .../syncope/core/logic/UserWorkflowTaskLogic.java  |  6 +-
 28 files changed, 342 insertions(+), 291 deletions(-)

diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java
index 3da062e..62c4cc6 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java
@@ -26,7 +26,6 @@ import java.util.stream.Collectors;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.to.RealmTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
@@ -46,6 +45,7 @@ import org.apache.syncope.core.provisioning.api.data.RealmDataBinder;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -121,8 +121,8 @@ public class RealmLogic extends AbstractTransactionalLogic<RealmTO> {
 
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.addAll(ResourceOperation.CREATE, realm.getResourceKeys());
-        List<PropagationTaskTO> tasks = propagationManager.createTasks(realm, propByRes, null);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, false);
+        List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, false);
 
         ProvisioningResult<RealmTO> result = new ProvisioningResult<>();
         result.setEntity(binder.getRealmTO(realm, true));
@@ -143,8 +143,8 @@ public class RealmLogic extends AbstractTransactionalLogic<RealmTO> {
         PropagationByResource propByRes = binder.update(realm, realmTO);
         realm = realmDAO.save(realm);
 
-        List<PropagationTaskTO> tasks = propagationManager.createTasks(realm, propByRes, null);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, false);
+        List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, false);
 
         ProvisioningResult<RealmTO> result = new ProvisioningResult<>();
         result.setEntity(binder.getRealmTO(realm, true));
@@ -184,8 +184,8 @@ public class RealmLogic extends AbstractTransactionalLogic<RealmTO> {
 
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.addAll(ResourceOperation.DELETE, realm.getResourceKeys());
-        List<PropagationTaskTO> tasks = propagationManager.createTasks(realm, propByRes, null);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, false);
+        List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, false);
 
         ProvisioningResult<RealmTO> result = new ProvisioningResult<>();
         result.setEntity(binder.getRealmTO(realm, true));
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
index 17f7376..7d231f9 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
@@ -60,6 +60,7 @@ import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
 import org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
 import org.apache.syncope.core.provisioning.java.job.TaskJob;
 import org.quartz.JobDataMap;
@@ -233,7 +234,20 @@ public class TaskLogic extends AbstractExecutableLogic<TaskTO> {
         ExecTO result = null;
         switch (taskUtil.getType()) {
             case PROPAGATION:
-                TaskExec propExec = taskExecutor.execute(binder.<PropagationTaskTO>getTaskTO(task, taskUtil, false));
+                PropagationTaskTO taskTO = binder.<PropagationTaskTO>getTaskTO(task, taskUtil, false);
+                PropagationTaskInfo taskInfo = new PropagationTaskInfo();
+                taskInfo.setKey(taskTO.getKey());
+                taskInfo.setOperation(taskTO.getOperation());
+                taskInfo.setConnObjectKey(taskTO.getConnObjectKey());
+                taskInfo.setOldConnObjectKey(taskTO.getOldConnObjectKey());
+                taskInfo.setAttributes(taskTO.getAttributes());
+                taskInfo.setResource(taskTO.getResource());
+                taskInfo.setObjectClassName(taskTO.getObjectClassName());
+                taskInfo.setAnyTypeKind(taskTO.getAnyTypeKind());
+                taskInfo.setAnyType(taskTO.getAnyType());
+                taskInfo.setEntityKey(taskTO.getEntityKey());
+
+                TaskExec propExec = taskExecutor.execute(taskInfo);
                 result = binder.getExecTO(propExec);
                 break;
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java
index 00c311b..3ca159d 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java
@@ -23,7 +23,6 @@ import java.util.List;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.UserPatch;
 import org.apache.syncope.common.lib.to.AttrTO;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.core.persistence.api.entity.Realm;
@@ -42,7 +41,7 @@ public interface PropagationManager {
      * @param noPropResourceKeys external resources performing not to be considered for propagation
      * @return list of propagation tasks
      */
-    List<PropagationTaskTO> getCreateTasks(
+    List<PropagationTaskInfo> getCreateTasks(
             AnyTypeKind kind,
             String key,
             Boolean enable,
@@ -61,7 +60,7 @@ public interface PropagationManager {
      * @param noPropResourceKeys external resources not to be considered for propagation
      * @return list of propagation tasks
      */
-    List<PropagationTaskTO> getUserCreateTasks(
+    List<PropagationTaskInfo> getUserCreateTasks(
             String key,
             String password,
             Boolean enable,
@@ -81,7 +80,7 @@ public interface PropagationManager {
      * @param noPropResourceKeys external resource keys not to be considered for propagation
      * @return list of propagation tasks
      */
-    List<PropagationTaskTO> getUpdateTasks(
+    List<PropagationTaskInfo> getUpdateTasks(
             AnyTypeKind kind,
             String key,
             boolean changePwd,
@@ -98,7 +97,7 @@ public interface PropagationManager {
      * @param noPropResourceKeys external resources not to be considered for propagation
      * @return list of propagation tasks
      */
-    List<PropagationTaskTO> getUserUpdateTasks(
+    List<PropagationTaskInfo> getUserUpdateTasks(
             WorkflowResult<Pair<UserPatch, Boolean>> wfResult,
             boolean changePwd,
             Collection<String> noPropResourceKeys);
@@ -110,7 +109,7 @@ public interface PropagationManager {
      * @param wfResult user to be propagated (and info associated), as per result from workflow
      * @return list of propagation tasks
      */
-    List<PropagationTaskTO> getUserUpdateTasks(WorkflowResult<Pair<UserPatch, Boolean>> wfResult);
+    List<PropagationTaskInfo> getUserUpdateTasks(WorkflowResult<Pair<UserPatch, Boolean>> wfResult);
 
     /**
      * Create the delete tasks for the any object from each resource associated, unless in {@code noPropResourceKeys}.
@@ -121,7 +120,7 @@ public interface PropagationManager {
      * @param noPropResourceKeys external resource keys not to be considered for propagation
      * @return list of propagation tasks
      */
-    List<PropagationTaskTO> getDeleteTasks(
+    List<PropagationTaskInfo> getDeleteTasks(
             AnyTypeKind kind,
             String key,
             PropagationByResource propByRes,
@@ -135,7 +134,7 @@ public interface PropagationManager {
      * @param noPropResourceKeys external resource keys not to be considered for propagation
      * @return list of propagation tasks
      */
-    List<PropagationTaskTO> createTasks(
+    List<PropagationTaskInfo> createTasks(
             Realm realm,
             PropagationByResource propByRes,
             Collection<String> noPropResourceKeys);
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationReporter.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationReporter.java
index 78ac2cc..814f75b 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationReporter.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationReporter.java
@@ -21,7 +21,6 @@ package org.apache.syncope.core.provisioning.api.propagation;
 import java.util.Collection;
 import java.util.List;
 import org.apache.syncope.common.lib.to.PropagationStatus;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.ExecStatus;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
 
@@ -34,22 +33,22 @@ public interface PropagationReporter {
      * Report propagation status after executions in case blocking failure (e.g. on priority resources).
      *
      * @param failingResource failing resource name
-     * @param tasks propagation tasks performed before failure
+     * @param taskInfos propagation tasks performed before failure
      */
-    void onPriorityResourceFailure(String failingResource, Collection<PropagationTaskTO> tasks);
+    void onPriorityResourceFailure(String failingResource, Collection<PropagationTaskInfo> taskInfos);
 
     /**
      * Report propagation status after executions in case of success or non-blocking failure
      * (e.g. on non-priority resources).
      *
-     * @param propagationTask propagation task
+     * @param taskInfo propagation task
      * @param execStatus propagation execution status
      * @param failureReason propagation execution failure message
      * @param beforeObj retrieved connector object before operation execution
      * @param afterObj retrieved connector object after operation execution
      */
     void onSuccessOrNonPriorityResourceFailures(
-            PropagationTaskTO propagationTask,
+            PropagationTaskInfo taskInfo,
             ExecStatus execStatus,
             String failureReason,
             ConnectorObject beforeObj,
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java
index 9c4f107..6dd3de8 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java
@@ -19,12 +19,11 @@
 package org.apache.syncope.core.provisioning.api.propagation;
 
 import java.util.concurrent.Callable;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 
 public interface PropagationTaskCallable extends Callable<TaskExec> {
 
-    void setTaskTO(PropagationTaskTO taskTO);
+    void setTaskInfo(PropagationTaskInfo taskInfo);
 
     void setReporter(PropagationReporter reporter);
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java
index e331aa7..a000d78 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java
@@ -44,28 +44,28 @@ public interface PropagationTaskExecutor {
     /**
      * Execute the given PropagationTask and returns the generated {@link TaskExec}.
      *
-     * @param task to be executed
+     * @param taskInfo to be executed
      * @return the generated TaskExec
      */
-    TaskExec execute(PropagationTaskTO task);
+    TaskExec execute(PropagationTaskInfo taskInfo);
 
     /**
      * Execute the given PropagationTask and returns the generated {@link TaskExec}.
      *
-     * @param task to be executed
+     * @param taskInfo to be executed
      * @param reporter to report propagation execution status
      * @return the generated TaskExec
      */
-    TaskExec execute(PropagationTaskTO task, PropagationReporter reporter);
+    TaskExec execute(PropagationTaskInfo taskInfo, PropagationReporter reporter);
 
     /**
      * Execute a collection of PropagationTask objects.
      * The process is interrupted as soon as the result of the communication with a resource with non-null priority is
      * in error.
      *
-     * @param tasks to be execute, in given order
+     * @param taskInfos to be execute, in given order
      * @param nullPriorityAsync asynchronously executes tasks related to resources with no priority
      * @return reporter to report propagation execution status
      */
-    PropagationReporter execute(Collection<PropagationTaskTO> tasks, boolean nullPriorityAsync);
+    PropagationReporter execute(Collection<PropagationTaskInfo> taskInfos, boolean nullPriorityAsync);
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java
similarity index 55%
copy from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java
copy to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java
index 9c4f107..322a163 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java
@@ -18,13 +18,28 @@
  */
 package org.apache.syncope.core.provisioning.api.propagation;
 
-import java.util.concurrent.Callable;
+import java.util.Optional;
 import org.apache.syncope.common.lib.to.PropagationTaskTO;
-import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
 
-public interface PropagationTaskCallable extends Callable<TaskExec> {
+public class PropagationTaskInfo extends PropagationTaskTO {
 
-    void setTaskTO(PropagationTaskTO taskTO);
+    private static final long serialVersionUID = -2879861567335503099L;
 
-    void setReporter(PropagationReporter reporter);
+    /**
+     * Object on External Resource before propagation takes place.
+     *
+     * null: beforeObj was not attempted to read
+     * not null, but not present: beforeObj was attempted to read, but not found
+     * not null and present: beforeObj value is available
+     */
+    private Optional<ConnectorObject> beforeObj;
+
+    public Optional<ConnectorObject> getBeforeObj() {
+        return beforeObj;
+    }
+
+    public void setBeforeObj(final Optional<ConnectorObject> beforeObj) {
+        this.beforeObj = beforeObj;
+    }
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
index 29447d5..c14f0dc 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
@@ -27,7 +27,6 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.AnyObjectPatch;
 import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.common.lib.types.ResourceOperation;
@@ -39,6 +38,7 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.workflow.api.AnyObjectWorkflowAdapter;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Propagation;
@@ -75,14 +75,14 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
 
         WorkflowResult<String> created = awfAdapter.create(anyObjectTO);
 
-        List<PropagationTaskTO> tasks = propagationManager.getCreateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getCreateTasks(
                 AnyTypeKind.ANY_OBJECT,
                 created.getResult(),
                 null,
                 created.getPropByRes(),
                 anyObjectTO.getVirAttrs(),
                 excludedResources);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return Pair.of(created.getResult(), propagationReporter.getStatuses());
     }
@@ -101,7 +101,7 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
 
         WorkflowResult<AnyObjectPatch> updated = awfAdapter.update(anyObjectPatch);
 
-        List<PropagationTaskTO> tasks = propagationManager.getUpdateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUpdateTasks(
                 AnyTypeKind.ANY_OBJECT,
                 updated.getResult().getKey(),
                 false,
@@ -109,7 +109,7 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
                 updated.getPropByRes(),
                 anyObjectPatch.getVirAttrs(),
                 excludedResources);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return Pair.of(updated.getResult(), propagationReporter.getStatuses());
     }
@@ -132,12 +132,12 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
         // information could only be available after awfAdapter.delete(), which
         // will also effectively remove user from db, thus making virtually
         // impossible by NotificationManager to fetch required user information
-        List<PropagationTaskTO> tasks = propagationManager.getDeleteTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getDeleteTasks(
                 AnyTypeKind.ANY_OBJECT,
                 key,
                 propByRes,
                 excludedResources);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         try {
             awfAdapter.delete(key);
@@ -165,7 +165,7 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.addAll(ResourceOperation.UPDATE, resources);
 
-        List<PropagationTaskTO> tasks = propagationManager.getUpdateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUpdateTasks(
                 AnyTypeKind.ANY_OBJECT,
                 key,
                 false,
@@ -173,7 +173,7 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
                 propByRes,
                 null,
                 null);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return propagationReporter.getStatuses();
     }
@@ -185,14 +185,14 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.addAll(ResourceOperation.DELETE, resources);
 
-        List<PropagationTaskTO> tasks = propagationManager.getDeleteTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getDeleteTasks(
                 AnyTypeKind.ANY_OBJECT,
                 key,
                 propByRes,
                 anyObjectDAO.findAllResourceKeys(key).stream().
                         filter(resource -> !resources.contains(resource)).
                         collect(Collectors.toList()));
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return propagationReporter.getStatuses();
     }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java
index 5ca7d23..7d20a03 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java
@@ -31,7 +31,6 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.GroupPatch;
 import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.to.GroupTO;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
@@ -43,6 +42,7 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
@@ -71,7 +71,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
     public Pair<String, List<PropagationStatus>> create(final GroupTO groupTO, final boolean nullPriorityAsync) {
         WorkflowResult<String> created = gwfAdapter.create(groupTO);
 
-        List<PropagationTaskTO> tasks = propagationManager.getCreateTasks(
+        List<PropagationTaskInfo> tasks = propagationManager.getCreateTasks(
                 AnyTypeKind.GROUP,
                 created.getResult(),
                 null,
@@ -97,7 +97,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
         groupTO.getPlainAttr(StringUtils.EMPTY).ifPresent(groupOwner
                 -> groupOwnerMap.put(created.getResult(), groupOwner.getValues().iterator().next()));
 
-        List<PropagationTaskTO> tasks = propagationManager.getCreateTasks(
+        List<PropagationTaskInfo> tasks = propagationManager.getCreateTasks(
                 AnyTypeKind.GROUP,
                 created.getResult(),
                 null,
@@ -123,7 +123,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
 
         WorkflowResult<GroupPatch> updated = gwfAdapter.update(groupPatch);
 
-        List<PropagationTaskTO> tasks = propagationManager.getUpdateTasks(
+        List<PropagationTaskInfo> tasks = propagationManager.getUpdateTasks(
                 AnyTypeKind.GROUP,
                 updated.getResult().getKey(),
                 false,
@@ -146,13 +146,13 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
     public List<PropagationStatus> delete(
             final String key, final Set<String> excludedResources, final boolean nullPriorityAsync) {
 
-        List<PropagationTaskTO> tasks = new ArrayList<>();
+        List<PropagationTaskInfo> taskInfos = new ArrayList<>();
 
         // Generate propagation tasks for deleting users and any objects from group resources, 
         // if they are on those resources only because of the reason being deleted (see SYNCOPE-357)
         groupDataBinder.findUsersWithTransitiveResources(key).entrySet().
                 forEach(entry -> {
-                    tasks.addAll(propagationManager.getDeleteTasks(
+                    taskInfos.addAll(propagationManager.getDeleteTasks(
                             AnyTypeKind.USER,
                             entry.getKey(),
                             entry.getValue(),
@@ -160,7 +160,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
                 });
         groupDataBinder.findAnyObjectsWithTransitiveResources(key).entrySet().
                 forEach(entry -> {
-                    tasks.addAll(propagationManager.getDeleteTasks(
+                    taskInfos.addAll(propagationManager.getDeleteTasks(
                             AnyTypeKind.ANY_OBJECT,
                             entry.getKey(),
                             entry.getValue(),
@@ -168,13 +168,13 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
                 });
 
         // Generate propagation tasks for deleting this group from resources
-        tasks.addAll(propagationManager.getDeleteTasks(
+        taskInfos.addAll(propagationManager.getDeleteTasks(
                 AnyTypeKind.GROUP,
                 key,
                 null,
                 null));
 
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         gwfAdapter.delete(key);
 
@@ -193,7 +193,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.addAll(ResourceOperation.UPDATE, resources);
 
-        List<PropagationTaskTO> tasks = propagationManager.getUpdateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUpdateTasks(
                 AnyTypeKind.GROUP,
                 key,
                 false,
@@ -201,7 +201,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
                 propByRes,
                 null,
                 null);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return propagationReporter.getStatuses();
     }
@@ -213,14 +213,14 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.addAll(ResourceOperation.DELETE, resources);
 
-        List<PropagationTaskTO> tasks = propagationManager.getDeleteTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getDeleteTasks(
                 AnyTypeKind.GROUP,
                 key,
                 propByRes,
                 groupDAO.findAllResourceKeys(key).stream().
                         filter(resource -> !resources.contains(resource)).
                         collect(Collectors.toList()));
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return propagationReporter.getStatuses();
     }
@@ -229,5 +229,4 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
     public String link(final GroupPatch groupPatch) {
         return gwfAdapter.update(groupPatch).getResult().getKey();
     }
-
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
index e507bbd..2d6cd2e 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
@@ -31,7 +31,6 @@ import org.apache.syncope.common.lib.patch.StatusPatch;
 import org.apache.syncope.common.lib.patch.StringPatchItem;
 import org.apache.syncope.common.lib.patch.UserPatch;
 import org.apache.syncope.common.lib.to.PropagationStatus;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.PatchOperation;
@@ -47,6 +46,7 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 import org.apache.syncope.core.provisioning.api.VirAttrHandler;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
 import org.slf4j.Logger;
@@ -99,14 +99,14 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         WorkflowResult<Pair<String, Boolean>> created =
                 uwfAdapter.create(userTO, disablePwdPolicyCheck, enabled, storePassword);
 
-        List<PropagationTaskTO> tasks = propagationManager.getUserCreateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUserCreateTasks(
                 created.getResult().getLeft(),
                 userTO.getPassword(),
                 created.getResult().getRight(),
                 created.getPropByRes(),
                 userTO.getVirAttrs(),
                 excludedResources);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return Pair.of(created.getResult().getLeft(), propagationReporter.getStatuses());
     }
@@ -115,8 +115,8 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
     public Pair<UserPatch, List<PropagationStatus>> update(final UserPatch userPatch, final boolean nullPriorityAsync) {
         WorkflowResult<Pair<UserPatch, Boolean>> updated = uwfAdapter.update(userPatch);
 
-        List<PropagationTaskTO> tasks = propagationManager.getUserUpdateTasks(updated);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(updated);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return Pair.of(updated.getResult().getLeft(), propagationReporter.getStatuses());
     }
@@ -173,9 +173,9 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
             }
         }
 
-        List<PropagationTaskTO> tasks = propagationManager.getUserUpdateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(
                 updated, updated.getResult().getLeft().getPassword() != null, excludedResources);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return Pair.of(updated.getResult().getLeft(), propagationReporter.getStatuses());
     }
@@ -198,12 +198,12 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         // information could only be available after uwfAdapter.delete(), which
         // will also effectively remove user from db, thus making virtually
         // impossible by NotificationManager to fetch required user information
-        List<PropagationTaskTO> tasks = propagationManager.getDeleteTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getDeleteTasks(
                 AnyTypeKind.USER,
                 key,
                 propByRes,
                 excludedResources);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         try {
             uwfAdapter.delete(key);
@@ -263,7 +263,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
 
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.addAll(ResourceOperation.UPDATE, statusPatch.getResources());
-        List<PropagationTaskTO> tasks = propagationManager.getUpdateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUpdateTasks(
                 AnyTypeKind.USER,
                 statusPatch.getKey(),
                 false,
@@ -271,7 +271,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
                 propByRes,
                 null,
                 null);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return propagationReporter.getStatuses();
     }
@@ -285,10 +285,10 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
             UserPatch userPatch = new UserPatch();
             userPatch.setKey(updated.getLeft().getResult());
 
-            List<PropagationTaskTO> tasks = propagationManager.getUserUpdateTasks(new WorkflowResult<>(
+            List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(new WorkflowResult<>(
                     Pair.of(userPatch, Boolean.FALSE),
                     updated.getLeft().getPropByRes(), updated.getLeft().getPerformedTasks()));
-            taskExecutor.execute(tasks, false);
+            taskExecutor.execute(taskInfos, false);
         }
     }
 
@@ -320,8 +320,8 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         WorkflowResult<Pair<UserPatch, Boolean>> wfResult = new WorkflowResult<>(
                 ImmutablePair.of(userPatch, (Boolean) null), propByRes, "update");
 
-        List<PropagationTaskTO> tasks = propagationManager.getUserUpdateTasks(wfResult, changePwd, null);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(wfResult, changePwd, null);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return propagationReporter.getStatuses();
     }
@@ -333,14 +333,14 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.set(ResourceOperation.DELETE, resources);
 
-        List<PropagationTaskTO> tasks = propagationManager.getDeleteTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getDeleteTasks(
                 AnyTypeKind.USER,
                 key,
                 propByRes,
                 userDAO.findAllResourceKeys(key).stream().
                         filter(resource -> !resources.contains(resource)).
                         collect(Collectors.toList()));
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return propagationReporter.getStatuses();
     }
@@ -354,8 +354,8 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
     public void confirmPasswordReset(final String key, final String token, final String password) {
         WorkflowResult<Pair<UserPatch, Boolean>> updated = uwfAdapter.confirmPasswordReset(key, token, password);
 
-        List<PropagationTaskTO> tasks = propagationManager.getUserUpdateTasks(updated);
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(updated);
 
-        taskExecutor.execute(tasks, false);
+        taskExecutor.execute(taskInfos, false);
     }
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
index 7127fa0..9587ec8 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
@@ -33,7 +33,6 @@ import java.util.concurrent.atomic.AtomicReference;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.collections.IteratorChain;
 import org.apache.syncope.common.lib.to.ExecTO;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.common.lib.types.AuditElements.Result;
 import org.apache.syncope.common.lib.types.ExecStatus;
@@ -69,6 +68,7 @@ import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheValue;
 import org.apache.syncope.core.provisioning.api.data.TaskDataBinder;
 import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
 import org.apache.syncope.core.spring.ImplementationManager;
@@ -346,29 +346,29 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
     }
 
     @Override
-    public TaskExec execute(final PropagationTaskTO task) {
-        return execute(task, null);
+    public TaskExec execute(final PropagationTaskInfo taskInfo) {
+        return execute(taskInfo, null);
     }
 
     @Override
-    public TaskExec execute(final PropagationTaskTO taskTO, final PropagationReporter reporter) {
+    public TaskExec execute(final PropagationTaskInfo taskInfo, final PropagationReporter reporter) {
         PropagationTask task;
-        if (taskTO.getKey() == null) {
+        if (taskInfo.getKey() == null) {
             task = entityFactory.newEntity(PropagationTask.class);
-            task.setResource(resourceDAO.find(taskTO.getResource()));
-            task.setObjectClassName(taskTO.getObjectClassName());
-            task.setAnyTypeKind(taskTO.getAnyTypeKind());
-            task.setAnyType(taskTO.getAnyType());
-            task.setEntityKey(taskTO.getEntityKey());
-            task.setOperation(taskTO.getOperation());
-            task.setConnObjectKey(taskTO.getConnObjectKey());
-            task.setOldConnObjectKey(taskTO.getOldConnObjectKey());
+            task.setResource(resourceDAO.find(taskInfo.getResource()));
+            task.setObjectClassName(taskInfo.getObjectClassName());
+            task.setAnyTypeKind(taskInfo.getAnyTypeKind());
+            task.setAnyType(taskInfo.getAnyType());
+            task.setEntityKey(taskInfo.getEntityKey());
+            task.setOperation(taskInfo.getOperation());
+            task.setConnObjectKey(taskInfo.getConnObjectKey());
+            task.setOldConnObjectKey(taskInfo.getOldConnObjectKey());
         } else {
-            task = taskDAO.find(taskTO.getKey());
+            task = taskDAO.find(taskInfo.getKey());
         }
         Set<Attribute> attributes = new HashSet<>();
-        if (StringUtils.isNotBlank(taskTO.getAttributes())) {
-            attributes.addAll(Arrays.asList(POJOHelper.deserialize(taskTO.getAttributes(), Attribute[].class)));
+        if (StringUtils.isNotBlank(taskInfo.getAttributes())) {
+            attributes.addAll(Arrays.asList(POJOHelper.deserialize(taskInfo.getAttributes(), Attribute[].class)));
         }
         task.setAttributes(attributes);
 
@@ -400,12 +400,16 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
             orgUnit = task.getResource().getOrgUnit();
             connector = connFactory.getConnector(task.getResource());
 
-            // Try to read remote object BEFORE any actual operation
-            beforeObj = provision == null && orgUnit == null
-                    ? null
-                    : orgUnit == null
-                            ? getRemoteObject(task, connector, provision, false)
-                            : getRemoteObject(task, connector, orgUnit, false);
+            if (taskInfo.getBeforeObj() == null) {
+                // Try to read remote object BEFORE any actual operation
+                beforeObj = provision == null && orgUnit == null
+                        ? null
+                        : orgUnit == null
+                                ? getRemoteObject(task, connector, provision, false)
+                                : getRemoteObject(task, connector, orgUnit, false);
+            } else if (taskInfo.getBeforeObj().isPresent()) {
+                beforeObj = taskInfo.getBeforeObj().get();
+            }
 
             for (PropagationActions action : actions) {
                 action.before(task, beforeObj);
@@ -503,7 +507,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
             }
 
             if (reporter != null) {
-                reporter.onSuccessOrNonPriorityResourceFailures(taskTO,
+                reporter.onSuccessOrNonPriorityResourceFailures(taskInfo,
                         ExecStatus.valueOf(execution.getStatus()),
                         failureReason,
                         beforeObj,
@@ -533,7 +537,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
                     result,
                     beforeObj,
                     new Object[] { execTO, afterObj },
-                    taskTO);
+                    taskInfo);
 
             auditManager.audit(
                     AuthContextUtils.getUsername(),
@@ -544,23 +548,26 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
                     result,
                     beforeObj,
                     new Object[] { execTO, afterObj },
-                    taskTO);
+                    taskInfo);
         }
 
         return execution;
     }
 
     protected abstract void doExecute(
-            Collection<PropagationTaskTO> tasks, PropagationReporter reporter, boolean nullPriorityAsync);
+            Collection<PropagationTaskInfo> taskInfos, PropagationReporter reporter, boolean nullPriorityAsync);
 
     @Override
-    public PropagationReporter execute(final Collection<PropagationTaskTO> tasks, final boolean nullPriorityAsync) {
+    public PropagationReporter execute(
+            final Collection<PropagationTaskInfo> taskInfos,
+            final boolean nullPriorityAsync) {
+
         PropagationReporter reporter = new DefaultPropagationReporter();
         try {
-            doExecute(tasks, reporter, nullPriorityAsync);
+            doExecute(taskInfos, reporter, nullPriorityAsync);
         } catch (PropagationException e) {
             LOG.error("Error propagation priority resource", e);
-            reporter.onPriorityResourceFailure(e.getResourceName(), tasks);
+            reporter.onPriorityResourceFailure(e.getResourceName(), taskInfos);
         }
 
         return reporter;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
index a2df73c..53b9fe8 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
@@ -24,10 +24,10 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
 import org.apache.syncope.common.lib.to.PropagationStatus;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.ExecStatus;
 import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
 import org.slf4j.Logger;
@@ -47,14 +47,14 @@ public class DefaultPropagationReporter implements PropagationReporter {
 
     @Override
     public void onSuccessOrNonPriorityResourceFailures(
-            final PropagationTaskTO taskTO,
+            final PropagationTaskInfo taskInfo,
             final ExecStatus executionStatus,
             final String failureReason,
             final ConnectorObject beforeObj,
             final ConnectorObject afterObj) {
 
         PropagationStatus status = new PropagationStatus();
-        status.setResource(taskTO.getResource());
+        status.setResource(taskInfo.getResource());
         status.setStatus(executionStatus);
         status.setFailureReason(failureReason);
 
@@ -70,10 +70,13 @@ public class DefaultPropagationReporter implements PropagationReporter {
     }
 
     @Override
-    public void onPriorityResourceFailure(final String failingResource, final Collection<PropagationTaskTO> tasks) {
+    public void onPriorityResourceFailure(
+            final String failingResource,
+            final Collection<PropagationTaskInfo> taskInfos) {
+
         LOG.debug("Propagation error: {} priority resource failed to propagate", failingResource);
 
-        Optional<PropagationTaskTO> propagationTask = tasks.stream().
+        Optional<PropagationTaskInfo> propagationTask = taskInfos.stream().
                 filter(task -> task.getResource().equals(failingResource)).findFirst();
 
         if (propagationTask.isPresent()) {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationTaskCallable.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationTaskCallable.java
index 9f7aff4..da9b97a 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationTaskCallable.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationTaskCallable.java
@@ -19,13 +19,13 @@
 package org.apache.syncope.core.provisioning.java.propagation;
 
 import java.util.Collection;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskCallable;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -48,7 +48,7 @@ public class DefaultPropagationTaskCallable implements PropagationTaskCallable {
 
     protected final Collection<? extends GrantedAuthority> authorities;
 
-    protected PropagationTaskTO taskTO;
+    protected PropagationTaskInfo taskInfo;
 
     protected PropagationReporter reporter;
 
@@ -60,8 +60,8 @@ public class DefaultPropagationTaskCallable implements PropagationTaskCallable {
     }
 
     @Override
-    public void setTaskTO(final PropagationTaskTO taskTO) {
-        this.taskTO = taskTO;
+    public void setTaskInfo(final PropagationTaskInfo taskInfo) {
+        this.taskInfo = taskInfo;
     }
 
     @Override
@@ -77,11 +77,11 @@ public class DefaultPropagationTaskCallable implements PropagationTaskCallable {
         auth.setDetails(new SyncopeAuthenticationDetails(domain));
         SecurityContextHolder.getContext().setAuthentication(auth);
 
-        LOG.debug("Execution started for {}", taskTO);
+        LOG.debug("Execution started for {}", taskInfo);
 
-        TaskExec execution = taskExecutor.execute(taskTO, reporter);
+        TaskExec execution = taskExecutor.execute(taskInfo, reporter);
 
-        LOG.debug("Execution completed for {}, {}", taskTO, execution);
+        LOG.debug("Execution completed for {}, {}", taskInfo, execution);
 
         return execution;
     }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
index 84339c8..bfdab7b 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
@@ -34,7 +34,6 @@ import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 import javax.annotation.Resource;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.ExecStatus;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
@@ -42,6 +41,7 @@ import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskCallable;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 
@@ -61,17 +61,17 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
      * Creates new instances of {@link PropagationTaskCallable} for usage with
      * {@link java.util.concurrent.CompletionService}.
      *
-     * @param taskTO to be executed
+     * @param taskInfo to be executed
      * @param reporter to report propagation execution status
      * @return new {@link PropagationTaskCallable} instance for usage with
      * {@link java.util.concurrent.CompletionService}
      */
     protected PropagationTaskCallable newPropagationTaskCallable(
-            final PropagationTaskTO taskTO, final PropagationReporter reporter) {
+            final PropagationTaskInfo taskInfo, final PropagationReporter reporter) {
 
         PropagationTaskCallable callable = (PropagationTaskCallable) ApplicationContextProvider.getBeanFactory().
                 createBean(DefaultPropagationTaskCallable.class, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
-        callable.setTaskTO(taskTO);
+        callable.setTaskInfo(taskInfo);
         callable.setReporter(reporter);
 
         return callable;
@@ -79,16 +79,16 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
 
     @Override
     protected void doExecute(
-            final Collection<PropagationTaskTO> tasks,
+            final Collection<PropagationTaskInfo> taskInfos,
             final PropagationReporter reporter,
             final boolean nullPriorityAsync) {
 
-        Map<PropagationTaskTO, ExternalResource> taskToResource = new HashMap<>(tasks.size());
-        List<PropagationTaskTO> prioritizedTasks = new ArrayList<>();
+        Map<PropagationTaskInfo, ExternalResource> taskToResource = new HashMap<>(taskInfos.size());
+        List<PropagationTaskInfo> prioritizedTasks = new ArrayList<>();
 
         int[] connRequestTimeout = { 60 };
 
-        tasks.forEach(task -> {
+        taskInfos.forEach(task -> {
             ExternalResource resource = resourceDAO.find(task.getResource());
             taskToResource.put(task, resource);
 
@@ -106,7 +106,7 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
         Collections.sort(prioritizedTasks, new PriorityComparator(taskToResource));
         LOG.debug("Propagation tasks sorted by priority, for serial execution: {}", prioritizedTasks);
 
-        Collection<PropagationTaskTO> concurrentTasks = tasks.stream().
+        Collection<PropagationTaskInfo> concurrentTasks = taskInfos.stream().
                 filter(task -> !prioritizedTasks.contains(task)).collect(Collectors.toSet());
         LOG.debug("Propagation tasks for concurrent execution: {}", concurrentTasks);
 
@@ -128,12 +128,12 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
 
         // then process non-priority resources concurrently...
         CompletionService<TaskExec> completionService = new ExecutorCompletionService<>(executor);
-        Map<PropagationTaskTO, Future<TaskExec>> nullPriority = new HashMap<>(concurrentTasks.size());
-        concurrentTasks.forEach(task -> {
+        Map<PropagationTaskInfo, Future<TaskExec>> nullPriority = new HashMap<>(concurrentTasks.size());
+        concurrentTasks.forEach(taskInfo -> {
             try {
                 nullPriority.put(
-                        task,
-                        completionService.submit(newPropagationTaskCallable(task, reporter)));
+                        taskInfo,
+                        completionService.submit(newPropagationTaskCallable(taskInfo, reporter)));
             } catch (Exception e) {
                 LOG.error("Unexpected exception", e);
             }
@@ -172,18 +172,18 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
     /**
      * Compare propagation tasks according to related ExternalResource's priority.
      */
-    protected static class PriorityComparator implements Comparator<PropagationTaskTO>, Serializable {
+    protected static class PriorityComparator implements Comparator<PropagationTaskInfo>, Serializable {
 
         private static final long serialVersionUID = -1969355670784448878L;
 
-        private final Map<PropagationTaskTO, ExternalResource> taskToResource;
+        private final Map<PropagationTaskInfo, ExternalResource> taskToResource;
 
-        public PriorityComparator(final Map<PropagationTaskTO, ExternalResource> taskToResource) {
+        public PriorityComparator(final Map<PropagationTaskInfo, ExternalResource> taskToResource) {
             this.taskToResource = taskToResource;
         }
 
         @Override
-        public int compare(final PropagationTaskTO task1, final PropagationTaskTO task2) {
+        public int compare(final PropagationTaskInfo task1, final PropagationTaskInfo task2) {
             int prop1 = taskToResource.get(task1).getPropagationPriority();
             int prop2 = taskToResource.get(task2).getPropagationPriority();
 
@@ -194,5 +194,4 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
                             : -1;
         }
     }
-
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
index cd851d5..c82b60f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
@@ -31,7 +31,6 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.UserPatch;
 import org.apache.syncope.common.lib.to.AttrTO;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.common.lib.types.ResourceOperation;
@@ -56,6 +55,7 @@ import org.apache.syncope.core.persistence.api.entity.resource.Item;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.provisioning.api.MappingManager;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
 import org.identityconnectors.framework.common.objects.Attribute;
@@ -133,7 +133,7 @@ public class PropagationManagerImpl implements PropagationManager {
     }
 
     @Override
-    public List<PropagationTaskTO> getCreateTasks(
+    public List<PropagationTaskInfo> getCreateTasks(
             final AnyTypeKind kind,
             final String key,
             final Boolean enable,
@@ -145,7 +145,7 @@ public class PropagationManagerImpl implements PropagationManager {
     }
 
     @Override
-    public List<PropagationTaskTO> getUserCreateTasks(
+    public List<PropagationTaskInfo> getUserCreateTasks(
             final String key,
             final String password,
             final Boolean enable,
@@ -156,7 +156,7 @@ public class PropagationManagerImpl implements PropagationManager {
         return getCreateTasks(userDAO.authFind(key), password, enable, propByRes, vAttrs, noPropResourceKeys);
     }
 
-    protected List<PropagationTaskTO> getCreateTasks(
+    protected List<PropagationTaskInfo> getCreateTasks(
             final Any<?> any,
             final String password,
             final Boolean enable,
@@ -165,7 +165,7 @@ public class PropagationManagerImpl implements PropagationManager {
             final Collection<String> noPropResourceKeys) {
 
         if (propByRes == null || propByRes.isEmpty()) {
-            return Collections.<PropagationTaskTO>emptyList();
+            return Collections.<PropagationTaskInfo>emptyList();
         }
 
         if (noPropResourceKeys != null) {
@@ -176,7 +176,7 @@ public class PropagationManagerImpl implements PropagationManager {
     }
 
     @Override
-    public List<PropagationTaskTO> getUpdateTasks(
+    public List<PropagationTaskInfo> getUpdateTasks(
             final AnyTypeKind kind,
             final String key,
             final boolean changePwd,
@@ -189,7 +189,7 @@ public class PropagationManagerImpl implements PropagationManager {
     }
 
     @Override
-    public List<PropagationTaskTO> getUserUpdateTasks(
+    public List<PropagationTaskInfo> getUserUpdateTasks(
             final WorkflowResult<Pair<UserPatch, Boolean>> wfResult,
             final boolean changePwd,
             final Collection<String> noPropResourceKeys) {
@@ -207,11 +207,11 @@ public class PropagationManagerImpl implements PropagationManager {
     }
 
     @Override
-    public List<PropagationTaskTO> getUserUpdateTasks(final WorkflowResult<Pair<UserPatch, Boolean>> wfResult) {
+    public List<PropagationTaskInfo> getUserUpdateTasks(final WorkflowResult<Pair<UserPatch, Boolean>> wfResult) {
         UserPatch userPatch = wfResult.getResult().getKey();
 
         // Propagate password update only to requested resources
-        List<PropagationTaskTO> tasks = new ArrayList<>();
+        List<PropagationTaskInfo> tasks = new ArrayList<>();
         if (userPatch.getPassword() == null) {
             // a. no specific password propagation request: generate propagation tasks for any resource associated
             tasks = getUserUpdateTasks(wfResult, false, null);
@@ -249,7 +249,7 @@ public class PropagationManagerImpl implements PropagationManager {
         return tasks;
     }
 
-    protected List<PropagationTaskTO> getUpdateTasks(
+    protected List<PropagationTaskInfo> getUpdateTasks(
             final Any<?> any,
             final String password,
             final boolean changePwd,
@@ -273,7 +273,7 @@ public class PropagationManagerImpl implements PropagationManager {
     }
 
     @Override
-    public List<PropagationTaskTO> getDeleteTasks(
+    public List<PropagationTaskInfo> getDeleteTasks(
             final AnyTypeKind kind,
             final String key,
             final PropagationByResource propByRes,
@@ -296,7 +296,7 @@ public class PropagationManagerImpl implements PropagationManager {
         return getDeleteTasks(any, localPropByRes, noPropResourceKeys);
     }
 
-    protected List<PropagationTaskTO> getDeleteTasks(
+    protected List<PropagationTaskInfo> getDeleteTasks(
             final Any<?> any,
             final PropagationByResource propByRes,
             final Collection<String> noPropResourceKeys) {
@@ -316,7 +316,7 @@ public class PropagationManagerImpl implements PropagationManager {
      * @param vAttrs virtual attributes to be set
      * @return list of propagation tasks created
      */
-    protected List<PropagationTaskTO> createTasks(final Any<?> any,
+    protected List<PropagationTaskInfo> createTasks(final Any<?> any,
             final String password, final boolean changePwd,
             final Boolean enable, final boolean deleteOnResource, final PropagationByResource propByRes,
             final Collection<AttrTO> vAttrs) {
@@ -363,7 +363,7 @@ public class PropagationManagerImpl implements PropagationManager {
         }
         LOG.debug("With virtual attributes {}:\n{}\n{}", any, propByRes, vAttrMap);
 
-        List<PropagationTaskTO> tasks = new ArrayList<>();
+        List<PropagationTaskInfo> tasks = new ArrayList<>();
 
         propByRes.asMap().forEach((resourceKey, operation) -> {
             ExternalResource resource = resourceDAO.find(resourceKey);
@@ -381,7 +381,7 @@ public class PropagationManagerImpl implements PropagationManager {
                 LOG.warn("Requesting propagation for {} but no propagation mapping provided for {}",
                         any.getType(), resource);
             } else {
-                PropagationTaskTO task = new PropagationTaskTO();
+                PropagationTaskInfo task = new PropagationTaskInfo();
                 task.setResource(resource.getKey());
                 task.setObjectClassName(provision.getObjectClass().getObjectClassValue());
                 task.setAnyTypeKind(any.getType().getKind());
@@ -435,7 +435,7 @@ public class PropagationManagerImpl implements PropagationManager {
     }
 
     @Override
-    public List<PropagationTaskTO> createTasks(
+    public List<PropagationTaskInfo> createTasks(
             final Realm realm,
             final PropagationByResource propByRes,
             final Collection<String> noPropResourceKeys) {
@@ -450,7 +450,7 @@ public class PropagationManagerImpl implements PropagationManager {
         propByRes.purge();
         LOG.debug("After purge {}:\n{}", realm, propByRes);
 
-        List<PropagationTaskTO> tasks = new ArrayList<>();
+        List<PropagationTaskInfo> tasks = new ArrayList<>();
 
         propByRes.asMap().forEach((resourceKey, operation) -> {
             ExternalResource resource = resourceDAO.find(resourceKey);
@@ -464,7 +464,7 @@ public class PropagationManagerImpl implements PropagationManager {
                 LOG.warn("Requesting propagation for {} but no ConnObjectLink provided for {}",
                         realm.getFullPath(), resource);
             } else {
-                PropagationTaskTO task = new PropagationTaskTO();
+                PropagationTaskInfo task = new PropagationTaskInfo();
                 task.setResource(resource.getKey());
                 task.setObjectClassName(orgUnit.getObjectClass().getObjectClassValue());
                 task.setEntityKey(realm.getKey());
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
index 8ad473e..d588985 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.stream.Collectors;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.syncope.common.lib.patch.AnyPatch;
@@ -47,9 +48,11 @@ import org.apache.syncope.core.provisioning.api.MappingManager;
 import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
 import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopePushResultHandler;
 import org.apache.syncope.core.provisioning.java.job.AfterHandlingJob;
+import org.apache.syncope.core.provisioning.java.propagation.DefaultPropagationReporter;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
 import org.quartz.JobExecutionException;
@@ -108,16 +111,20 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
         propByRes.add(ResourceOperation.UPDATE, profile.getTask().getResource().getKey());
         propByRes.addOldConnObjectKey(profile.getTask().getResource().getKey(), beforeObj.getUid().getUidValue());
 
-        PropagationReporter reporter = taskExecutor.execute(propagationManager.getUpdateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUpdateTasks(
                 any.getType().getKind(),
                 any.getKey(),
                 changepwd,
                 enable,
                 propByRes,
                 null,
-                noPropResources),
-                false);
-        reportPropagation(result, reporter);
+                noPropResources);
+        if (!taskInfos.isEmpty()) {
+            taskInfos.get(0).setBeforeObj(Optional.of(beforeObj));
+            PropagationReporter reporter = new DefaultPropagationReporter();
+            taskExecutor.execute(taskInfos.get(0), reporter);
+            reportPropagation(result, reporter);
+        }
     }
 
     protected void deprovision(final Any<?> any, final ConnectorObject beforeObj, final ProvisioningReport result) {
@@ -130,13 +137,17 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
         propByRes.add(ResourceOperation.DELETE, profile.getTask().getResource().getKey());
         propByRes.addOldConnObjectKey(profile.getTask().getResource().getKey(), beforeObj.getUid().getUidValue());
 
-        PropagationReporter reporter = taskExecutor.execute(propagationManager.getDeleteTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getDeleteTasks(
                 any.getType().getKind(),
                 any.getKey(),
                 propByRes,
-                noPropResources),
-                false);
-        reportPropagation(result, reporter);
+                noPropResources);
+        if (!taskInfos.isEmpty()) {
+            taskInfos.get(0).setBeforeObj(Optional.of(beforeObj));
+            PropagationReporter reporter = new DefaultPropagationReporter();
+            taskExecutor.execute(taskInfos.get(0), reporter);
+            reportPropagation(result, reporter);
+        }
     }
 
     protected void provision(final Any<?> any, final Boolean enable, final ProvisioningReport result) {
@@ -148,15 +159,19 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
 
-        PropagationReporter reporter = taskExecutor.execute(propagationManager.getCreateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getCreateTasks(
                 any.getType().getKind(),
                 any.getKey(),
                 enable,
                 propByRes,
                 before.getVirAttrs(),
-                noPropResources),
-                false);
-        reportPropagation(result, reporter);
+                noPropResources);
+        if (!taskInfos.isEmpty()) {
+            taskInfos.get(0).setBeforeObj(Optional.ofNullable(null));
+            PropagationReporter reporter = new DefaultPropagationReporter();
+            taskExecutor.execute(taskInfos.get(0), reporter);
+            reportPropagation(result, reporter);
+        }
     }
 
     protected void link(final Any<?> any, final boolean unlink, final ProvisioningReport result) {
@@ -436,15 +451,21 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
                 if (result.getStatus() == null) {
                     result.setStatus(ProvisioningReport.Status.SUCCESS);
                 }
-                resultStatus = AuditElements.Result.SUCCESS;
-                output = pushUtils.findByConnObjectKey(profile.getConnector(), any, provision);
+
+                if (notificationsAvailable || auditRequested) {
+                    resultStatus = AuditElements.Result.SUCCESS;
+                    output = pushUtils.findByConnObjectKey(profile.getConnector(), any, provision);
+                }
             } catch (IgnoreProvisionException e) {
                 throw e;
             } catch (Exception e) {
                 result.setStatus(ProvisioningReport.Status.FAILURE);
                 result.setMessage(ExceptionUtils.getRootCauseMessage(e));
-                resultStatus = AuditElements.Result.FAILURE;
-                output = e;
+
+                if (notificationsAvailable || auditRequested) {
+                    resultStatus = AuditElements.Result.FAILURE;
+                    output = e;
+                }
 
                 LOG.warn("Error pushing {} towards {}", any, profile.getTask().getResource(), e);
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
index e0dd728..7bef80e 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
@@ -24,7 +24,6 @@ import java.util.List;
 import java.util.Set;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.to.RealmTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.AuditElements;
@@ -43,6 +42,7 @@ import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
 import org.apache.syncope.core.persistence.api.entity.task.PullTask;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
@@ -238,8 +238,8 @@ public class DefaultRealmPullResultHandler
             PropagationByResource propByRes = new PropagationByResource();
             propByRes.addAll(ResourceOperation.CREATE, realm.getResourceKeys());
             if (unmatchingRule == UnmatchingRule.ASSIGN) {
-                List<PropagationTaskTO> tasks = propagationManager.createTasks(realm, propByRes, null);
-                taskExecutor.execute(tasks, false);
+                List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
+                taskExecutor.execute(taskInfos, false);
             }
 
             RealmTO actual = binder.getRealmTO(realm, true);
@@ -324,8 +324,8 @@ public class DefaultRealmPullResultHandler
                         realm = realmDAO.save(realm);
                         RealmTO updated = binder.getRealmTO(realm, true);
 
-                        List<PropagationTaskTO> tasks = propagationManager.createTasks(realm, propByRes, null);
-                        taskExecutor.execute(tasks, false);
+                        List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
+                        taskExecutor.execute(taskInfos, false);
 
                         for (PullActions action : profile.getActions()) {
                             action.after(profile, delta, updated, result);
@@ -608,8 +608,8 @@ public class DefaultRealmPullResultHandler
 
                         PropagationByResource propByRes = new PropagationByResource();
                         propByRes.addAll(ResourceOperation.DELETE, realm.getResourceKeys());
-                        List<PropagationTaskTO> tasks = propagationManager.createTasks(realm, propByRes, null);
-                        taskExecutor.execute(tasks, false);
+                        List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
+                        taskExecutor.execute(taskInfos, false);
 
                         realmDAO.delete(realm);
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
index 711d49b..e631cb9 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
@@ -42,11 +42,13 @@ import org.apache.syncope.core.provisioning.api.MappingManager;
 import org.apache.syncope.core.provisioning.api.TimeoutException;
 import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
 import org.apache.syncope.core.provisioning.api.pushpull.RealmPushResultHandler;
 import org.apache.syncope.core.provisioning.java.job.AfterHandlingJob;
+import org.apache.syncope.core.provisioning.java.propagation.DefaultPropagationReporter;
 import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.identityconnectors.framework.common.objects.AttributeBuilder;
@@ -99,28 +101,36 @@ public class DefaultRealmPushResultHandler
         }
     }
 
-    private Realm update(final RealmTO realmTO, final ProvisioningReport result) {
+    private Realm update(final RealmTO realmTO, final ConnectorObject beforeObj, final ProvisioningReport result) {
         Realm realm = realmDAO.findByFullPath(realmTO.getFullPath());
         PropagationByResource propByRes = binder.update(realm, realmTO);
         realm = realmDAO.save(realm);
 
-        PropagationReporter reporter = taskExecutor.execute(
-                propagationManager.createTasks(realm, propByRes, null), false);
-        reportPropagation(result, reporter);
+        List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
+        if (!taskInfos.isEmpty()) {
+            taskInfos.get(0).setBeforeObj(Optional.ofNullable(beforeObj));
+            PropagationReporter reporter = new DefaultPropagationReporter();
+            taskExecutor.execute(taskInfos.get(0), reporter);
+            reportPropagation(result, reporter);
+        }
 
         return realm;
     }
 
-    private void deprovision(final Realm realm, final ProvisioningReport result) {
+    private void deprovision(final Realm realm, final ConnectorObject beforeObj, final ProvisioningReport result) {
         List<String> noPropResources = new ArrayList<>(realm.getResourceKeys());
         noPropResources.remove(profile.getTask().getResource().getKey());
 
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.addAll(ResourceOperation.DELETE, realm.getResourceKeys());
 
-        PropagationReporter reporter = taskExecutor.execute(
-                propagationManager.createTasks(realm, propByRes, noPropResources), false);
-        reportPropagation(result, reporter);
+        List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, noPropResources);
+        if (!taskInfos.isEmpty()) {
+            taskInfos.get(0).setBeforeObj(Optional.ofNullable(beforeObj));
+            PropagationReporter reporter = new DefaultPropagationReporter();
+            taskExecutor.execute(taskInfos.get(0), reporter);
+            reportPropagation(result, reporter);
+        }
     }
 
     private void provision(final Realm realm, final ProvisioningReport result) {
@@ -143,21 +153,21 @@ public class DefaultRealmPushResultHandler
             realmTO.getResources().add(profile.getTask().getResource().getKey());
         }
 
-        update(realmTO, result);
+        update(realmTO, null, result);
     }
 
-    private void unassign(final Realm realm, final ProvisioningReport result) {
+    private void unassign(final Realm realm, final ConnectorObject beforeObj, final ProvisioningReport result) {
         RealmTO realmTO = binder.getRealmTO(realm, true);
         realmTO.getResources().remove(profile.getTask().getResource().getKey());
 
-        deprovision(update(realmTO, result), result);
+        deprovision(update(realmTO, beforeObj, result), beforeObj, result);
     }
 
     private void assign(final Realm realm, final ProvisioningReport result) {
         RealmTO realmTO = binder.getRealmTO(realm, true);
         realmTO.getResources().add(profile.getTask().getResource().getKey());
 
-        provision(update(realmTO, result), result);
+        provision(update(realmTO, null, result), result);
     }
 
     protected ConnectorObject getRemoteObject(
@@ -304,7 +314,7 @@ public class DefaultRealmPushResultHandler
                                 LOG.debug("PushTask not configured for update");
                                 result.setStatus(ProvisioningReport.Status.IGNORE);
                             } else {
-                                update(binder.getRealmTO(realm, true), result);
+                                update(binder.getRealmTO(realm, true), beforeObj, result);
                             }
 
                             break;
@@ -318,7 +328,7 @@ public class DefaultRealmPushResultHandler
                                 LOG.debug("PushTask not configured for delete");
                                 result.setStatus(ProvisioningReport.Status.IGNORE);
                             } else {
-                                deprovision(realm, result);
+                                deprovision(realm, beforeObj, result);
                             }
 
                             break;
@@ -332,7 +342,7 @@ public class DefaultRealmPushResultHandler
                                 LOG.debug("PushTask not configured for delete");
                                 result.setStatus(ProvisioningReport.Status.IGNORE);
                             } else {
-                                unassign(realm, result);
+                                unassign(realm, beforeObj, result);
                             }
 
                             break;
@@ -382,22 +392,28 @@ public class DefaultRealmPushResultHandler
                 if (result.getStatus() == null) {
                     result.setStatus(ProvisioningReport.Status.SUCCESS);
                 }
-                resultStatus = AuditElements.Result.SUCCESS;
-                if (connObjectKey.isPresent() && connObjecKeyValue.isPresent()) {
-                    output = getRemoteObject(
-                            orgUnit.getObjectClass(),
-                            connObjectKey.get().getExtAttrName(),
-                            connObjecKeyValue.get(),
-                            orgUnit.isIgnoreCaseMatch(),
-                            orgUnit.getItems().iterator());
+
+                if (notificationsAvailable || auditRequested) {
+                    resultStatus = AuditElements.Result.SUCCESS;
+                    if (connObjectKey.isPresent() && connObjecKeyValue.isPresent()) {
+                        output = getRemoteObject(
+                                orgUnit.getObjectClass(),
+                                connObjectKey.get().getExtAttrName(),
+                                connObjecKeyValue.get(),
+                                orgUnit.isIgnoreCaseMatch(),
+                                orgUnit.getItems().iterator());
+                    }
                 }
             } catch (IgnoreProvisionException e) {
                 throw e;
             } catch (Exception e) {
                 result.setStatus(ProvisioningReport.Status.FAILURE);
                 result.setMessage(ExceptionUtils.getRootCauseMessage(e));
-                resultStatus = AuditElements.Result.FAILURE;
-                output = e;
+
+                if (notificationsAvailable || auditRequested) {
+                    resultStatus = AuditElements.Result.FAILURE;
+                    output = e;
+                }
 
                 LOG.warn("Error pushing {} towards {}", realm, profile.getTask().getResource(), e);
 
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ConfirmPasswordResetProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ConfirmPasswordResetProducer.java
index 3d35c93..7347004 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ConfirmPasswordResetProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ConfirmPasswordResetProducer.java
@@ -24,9 +24,9 @@ import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.UserPatch;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 
 public class ConfirmPasswordResetProducer extends AbstractProducer {
 
@@ -41,10 +41,9 @@ public class ConfirmPasswordResetProducer extends AbstractProducer {
             WorkflowResult<Pair<UserPatch, Boolean>> updated =
                     (WorkflowResult<Pair<UserPatch, Boolean>>) exchange.getIn().getBody();
 
-            List<PropagationTaskTO> tasks = getPropagationManager().getUserUpdateTasks(updated);
+            List<PropagationTaskInfo> taskInfos = getPropagationManager().getUserUpdateTasks(updated);
 
-            getPropagationTaskExecutor().execute(tasks, false);
+            getPropagationTaskExecutor().execute(taskInfos, false);
         }
     }
-
 }
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java
index 27ab612..95c1b2d 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java
@@ -21,7 +21,6 @@ package org.apache.syncope.core.provisioning.camel.producer;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.commons.lang3.StringUtils;
@@ -29,11 +28,11 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.GroupTO;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 
 public class CreateProducer extends AbstractProducer {
 
@@ -53,18 +52,17 @@ public class CreateProducer extends AbstractProducer {
                 WorkflowResult<Pair<String, Boolean>> created =
                         (WorkflowResult<Pair<String, Boolean>>) exchange.getIn().getBody();
 
-                List<PropagationTaskTO> tasks = getPropagationManager().getUserCreateTasks(
+                List<PropagationTaskInfo> taskInfos = getPropagationManager().getUserCreateTasks(
                         created.getResult().getKey(),
                         ((UserTO) actual).getPassword(),
                         created.getResult().getValue(),
                         created.getPropByRes(),
                         ((UserTO) actual).getVirAttrs(),
                         excludedResources);
-                PropagationReporter propagationReporter =
-                        getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                PropagationReporter reporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
 
                 exchange.getOut().setBody(
-                        Pair.of(created.getResult().getKey(), propagationReporter.getStatuses()));
+                        Pair.of(created.getResult().getKey(), reporter.getStatuses()));
             } else if (actual instanceof AnyTO) {
                 WorkflowResult<String> created = (WorkflowResult<String>) exchange.getIn().getBody();
 
@@ -73,31 +71,29 @@ public class CreateProducer extends AbstractProducer {
                     ((GroupTO) actual).getPlainAttr(StringUtils.EMPTY).ifPresent(groupOwner
                             -> groupOwnerMap.put(created.getResult(), groupOwner.getValues().iterator().next()));
 
-                    List<PropagationTaskTO> tasks = getPropagationManager().getCreateTasks(
+                    List<PropagationTaskInfo> taskInfos = getPropagationManager().getCreateTasks(
                             AnyTypeKind.GROUP,
                             created.getResult(),
                             null,
                             created.getPropByRes(),
                             ((AnyTO) actual).getVirAttrs(),
                             excludedResources);
-                    getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                    getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
 
                     exchange.getOut().setBody(Pair.of(created.getResult(), null));
                 } else {
-                    List<PropagationTaskTO> tasks = getPropagationManager().getCreateTasks(
+                    List<PropagationTaskInfo> taskInfos = getPropagationManager().getCreateTasks(
                             actual instanceof AnyObjectTO ? AnyTypeKind.ANY_OBJECT : AnyTypeKind.GROUP,
                             created.getResult(),
                             null,
                             created.getPropByRes(),
                             ((AnyTO) actual).getVirAttrs(),
                             excludedResources);
-                    PropagationReporter propagationReporter =
-                            getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                    PropagationReporter reporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
 
-                    exchange.getOut().setBody(Pair.of(created.getResult(), propagationReporter.getStatuses()));
+                    exchange.getOut().setBody(Pair.of(created.getResult(), reporter.getStatuses()));
                 }
             }
         }
     }
-
 }
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java
index 2f22b1b..f93fec9 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java
@@ -22,16 +22,15 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 
 public class DeleteProducer extends AbstractProducer {
 
@@ -58,8 +57,8 @@ public class DeleteProducer extends AbstractProducer {
         Boolean nullPriorityAsync = exchange.getProperty("nullPriorityAsync", Boolean.class);
 
         if (null != getAnyTypeKind()) {
-            List<PropagationTaskTO> tasks;
-            PropagationReporter propagationReporter;
+            List<PropagationTaskInfo> taskInfos;
+            PropagationReporter reporter;
             switch (getAnyTypeKind()) {
                 case USER:
                     PropagationByResource propByRes = new PropagationByResource();
@@ -69,54 +68,52 @@ public class DeleteProducer extends AbstractProducer {
                     // information could only be available after uwfAdapter.delete(), which
                     // will also effectively remove user from db, thus making virtually
                     // impossible by NotificationManager to fetch required user information
-                    tasks = getPropagationManager().getDeleteTasks(
+                    taskInfos = getPropagationManager().getDeleteTasks(
                             AnyTypeKind.USER,
                             key,
                             propByRes,
                             excludedResources);
-                    propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
-                    exchange.setProperty("statuses", propagationReporter.getStatuses());
+                    reporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
+                    exchange.setProperty("statuses", reporter.getStatuses());
                     break;
 
                 case GROUP:
-                    tasks = new ArrayList<>();
+                    taskInfos = new ArrayList<>();
                     // Generate propagation tasks for deleting users from group resources, if they are on those
                     // resources only because of the reason being deleted (see SYNCOPE-357)
                     for (Map.Entry<String, PropagationByResource> entry
                             : groupDataBinder.findUsersWithTransitiveResources(key).entrySet()) {
 
-                        tasks.addAll(getPropagationManager().getDeleteTasks(
+                        taskInfos.addAll(getPropagationManager().getDeleteTasks(
                                 AnyTypeKind.USER,
                                 entry.getKey(),
                                 entry.getValue(),
                                 excludedResources));
                     }
-                    for (Map.Entry<String, PropagationByResource> entry
-                            : groupDataBinder.findAnyObjectsWithTransitiveResources(key).entrySet()) {
-
-                        tasks.addAll(getPropagationManager().getDeleteTasks(
+                    groupDataBinder.findAnyObjectsWithTransitiveResources(key).forEach((k, pbr) -> {
+                        taskInfos.addAll(getPropagationManager().getDeleteTasks(
                                 AnyTypeKind.ANY_OBJECT,
-                                entry.getKey(),
-                                entry.getValue(),
+                                k,
+                                pbr,
                                 excludedResources));
-                    }       // Generate propagation tasks for deleting this group from resources
-                    tasks.addAll(getPropagationManager().getDeleteTasks(
+                    }); // Generate propagation tasks for deleting this group from resources
+                    taskInfos.addAll(getPropagationManager().getDeleteTasks(
                             AnyTypeKind.GROUP,
                             key,
                             null,
                             null));
-                    propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
-                    exchange.setProperty("statuses", propagationReporter.getStatuses());
+                    reporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
+                    exchange.setProperty("statuses", reporter.getStatuses());
                     break;
 
                 case ANY_OBJECT:
-                    tasks = getPropagationManager().getDeleteTasks(
+                    taskInfos = getPropagationManager().getDeleteTasks(
                             AnyTypeKind.ANY_OBJECT,
                             key,
                             null,
                             excludedResources);
-                    propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
-                    exchange.setProperty("statuses", propagationReporter.getStatuses());
+                    reporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
+                    exchange.setProperty("statuses", reporter.getStatuses());
                     break;
 
                 default:
@@ -124,5 +121,4 @@ public class DeleteProducer extends AbstractProducer {
             }
         }
     }
-
 }
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java
index 5521782..2ff8dd5 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java
@@ -20,10 +20,8 @@ package org.apache.syncope.core.provisioning.camel.producer;
 
 import java.util.List;
 import java.util.stream.Collectors;
-
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
@@ -31,6 +29,7 @@ import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 
 public class DeprovisionProducer extends AbstractProducer {
 
@@ -62,42 +61,42 @@ public class DeprovisionProducer extends AbstractProducer {
 
         if (null != getAnyTypeKind()) {
             PropagationByResource propByRes = new PropagationByResource();
-            List<PropagationTaskTO> tasks;
+            List<PropagationTaskInfo> taskInfos;
             PropagationReporter propagationReporter;
             switch (getAnyTypeKind()) {
                 case USER:
                     propByRes.set(ResourceOperation.DELETE, resources);
-                    tasks = getPropagationManager().getDeleteTasks(
+                    taskInfos = getPropagationManager().getDeleteTasks(
                             AnyTypeKind.USER,
                             key,
                             propByRes,
                             userDAO.findAllResourceKeys(key).stream().
                                     filter(resource -> !resources.contains(resource)).collect(Collectors.toList()));
-                    propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                    propagationReporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
                     exchange.getOut().setBody(propagationReporter.getStatuses());
                     break;
 
                 case GROUP:
                     propByRes.addAll(ResourceOperation.DELETE, resources);
-                    tasks = getPropagationManager().getDeleteTasks(
+                    taskInfos = getPropagationManager().getDeleteTasks(
                             AnyTypeKind.GROUP,
                             key,
                             propByRes,
                             groupDAO.findAllResourceKeys(key).stream().
                                     filter(resource -> !resources.contains(resource)).collect(Collectors.toList()));
-                    propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                    propagationReporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
                     exchange.getOut().setBody(propagationReporter.getStatuses());
                     break;
 
                 case ANY_OBJECT:
                     propByRes.addAll(ResourceOperation.DELETE, resources);
-                    tasks = getPropagationManager().getDeleteTasks(
+                    taskInfos = getPropagationManager().getDeleteTasks(
                             AnyTypeKind.ANY_OBJECT,
                             key,
                             propByRes,
                             anyObjectDAO.findAllResourceKeys(key).stream().
                                     filter(resource -> !resources.contains(resource)).collect(Collectors.toList()));
-                    propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                    propagationReporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
                     exchange.getOut().setBody(propagationReporter.getStatuses());
                     break;
 
@@ -106,5 +105,4 @@ public class DeprovisionProducer extends AbstractProducer {
             }
         }
     }
-
 }
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ProvisionProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ProvisionProducer.java
index fe533c1..bec4907 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ProvisionProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ProvisionProducer.java
@@ -20,7 +20,6 @@ package org.apache.syncope.core.provisioning.camel.producer;
 
 import java.util.List;
 import java.util.stream.Collectors;
-
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.commons.lang3.tuple.ImmutablePair;
@@ -28,13 +27,13 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.PasswordPatch;
 import org.apache.syncope.common.lib.patch.StringPatchItem;
 import org.apache.syncope.common.lib.patch.UserPatch;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 
 public class ProvisionProducer extends AbstractProducer {
 
@@ -70,10 +69,10 @@ public class ProvisionProducer extends AbstractProducer {
             WorkflowResult<Pair<UserPatch, Boolean>> wfResult = new WorkflowResult<>(
                     ImmutablePair.of(userPatch, (Boolean) null), propByRes, "update");
 
-            List<PropagationTaskTO> tasks = getPropagationManager().getUserUpdateTasks(wfResult, changePwd, null);
-            PropagationReporter propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+            List<PropagationTaskInfo> taskInfos = getPropagationManager().getUserUpdateTasks(wfResult, changePwd, null);
+            PropagationReporter reporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
 
-            exchange.getOut().setBody(propagationReporter.getStatuses());
+            exchange.getOut().setBody(reporter.getStatuses());
         } else {
             PropagationByResource propByRes = new PropagationByResource();
             propByRes.addAll(ResourceOperation.UPDATE, resources);
@@ -83,7 +82,7 @@ public class ProvisionProducer extends AbstractProducer {
                 anyTypeKind = getAnyTypeKind();
             }
 
-            List<PropagationTaskTO> tasks = getPropagationManager().getUpdateTasks(
+            List<PropagationTaskInfo> taskInfos = getPropagationManager().getUpdateTasks(
                     anyTypeKind,
                     key,
                     false,
@@ -91,10 +90,9 @@ public class ProvisionProducer extends AbstractProducer {
                     propByRes,
                     null,
                     null);
-            PropagationReporter propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+            PropagationReporter reporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
 
-            exchange.getOut().setBody(propagationReporter.getStatuses());
+            exchange.getOut().setBody(reporter.getStatuses());
         }
     }
-
 }
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/StatusProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/StatusProducer.java
index 6aae0a7..9353b72 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/StatusProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/StatusProducer.java
@@ -21,13 +21,11 @@ package org.apache.syncope.core.provisioning.camel.producer;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.StatusPatch;
 import org.apache.syncope.common.lib.patch.UserPatch;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.common.lib.types.StatusPatchType;
@@ -36,6 +34,7 @@ import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
 
 public class StatusProducer extends AbstractProducer {
@@ -92,7 +91,7 @@ public class StatusProducer extends AbstractProducer {
 
             PropagationByResource propByRes = new PropagationByResource();
             propByRes.addAll(ResourceOperation.UPDATE, statusPatch.getResources());
-            List<PropagationTaskTO> tasks = getPropagationManager().getUpdateTasks(
+            List<PropagationTaskInfo> taskInfos = getPropagationManager().getUpdateTasks(
                     AnyTypeKind.USER,
                     statusPatch.getKey(),
                     false,
@@ -100,9 +99,9 @@ public class StatusProducer extends AbstractProducer {
                     propByRes,
                     null,
                     null);
-            PropagationReporter propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+            PropagationReporter reporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
 
-            exchange.getOut().setBody(Pair.of(updated.getResult(), propagationReporter.getStatuses()));
+            exchange.getOut().setBody(Pair.of(updated.getResult(), reporter.getStatuses()));
         }
     }
 }
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/SuspendProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/SuspendProducer.java
index 27feae8..6ad268c 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/SuspendProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/SuspendProducer.java
@@ -24,9 +24,9 @@ import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.UserPatch;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 
 public class SuspendProducer extends AbstractProducer {
 
@@ -46,13 +46,12 @@ public class SuspendProducer extends AbstractProducer {
                 UserPatch userPatch = new UserPatch();
                 userPatch.setKey(updated.getKey().getResult());
 
-                List<PropagationTaskTO> tasks = getPropagationManager().getUserUpdateTasks(
+                List<PropagationTaskInfo> taskInfos = getPropagationManager().getUserUpdateTasks(
                         new WorkflowResult<>(
                                 Pair.of(userPatch, Boolean.FALSE),
                                 updated.getKey().getPropByRes(), updated.getKey().getPerformedTasks()));
-                getPropagationTaskExecutor().execute(tasks, false);
+                getPropagationTaskExecutor().execute(taskInfos, false);
             }
         }
     }
-
 }
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java
index f89182b..3cfa9ff 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java
@@ -20,17 +20,16 @@ package org.apache.syncope.core.provisioning.camel.producer;
 
 import java.util.List;
 import java.util.Set;
-
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.AnyObjectPatch;
 import org.apache.syncope.common.lib.patch.AnyPatch;
 import org.apache.syncope.common.lib.patch.UserPatch;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 
 public class UpdateProducer extends AbstractProducer {
 
@@ -50,23 +49,21 @@ public class UpdateProducer extends AbstractProducer {
                 WorkflowResult<Pair<UserPatch, Boolean>> updated =
                         (WorkflowResult<Pair<UserPatch, Boolean>>) exchange.getIn().getBody();
 
-                List<PropagationTaskTO> tasks;
+                List<PropagationTaskInfo> taskInfos;
                 if (isPull()) {
                     boolean passwordNotNull = updated.getResult().getKey().getPassword() != null;
-                    tasks = getPropagationManager().getUserUpdateTasks(updated, passwordNotNull, excludedResources);
+                    taskInfos = getPropagationManager().getUserUpdateTasks(updated, passwordNotNull, excludedResources);
                 } else {
-                    tasks = getPropagationManager().getUserUpdateTasks(updated);
+                    taskInfos = getPropagationManager().getUserUpdateTasks(updated);
                 }
-                PropagationReporter propagationReporter =
-                        getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                PropagationReporter reporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
 
-                exchange.getOut().setBody(Pair.of(
-                        updated.getResult().getLeft(), propagationReporter.getStatuses()));
+                exchange.getOut().setBody(Pair.of(updated.getResult().getLeft(), reporter.getStatuses()));
             } else if (actual instanceof AnyPatch) {
                 WorkflowResult<? extends AnyPatch> updated =
                         (WorkflowResult<? extends AnyPatch>) exchange.getIn().getBody();
 
-                List<PropagationTaskTO> tasks = getPropagationManager().getUpdateTasks(
+                List<PropagationTaskInfo> taskInfos = getPropagationManager().getUpdateTasks(
                         actual instanceof AnyObjectPatch ? AnyTypeKind.ANY_OBJECT : AnyTypeKind.GROUP,
                         updated.getResult().getKey(),
                         false,
@@ -74,12 +71,10 @@ public class UpdateProducer extends AbstractProducer {
                         updated.getPropByRes(),
                         ((AnyPatch) actual).getVirAttrs(),
                         excludedResources);
-                PropagationReporter propagationReporter =
-                        getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                PropagationReporter reporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
 
-                exchange.getOut().setBody(Pair.of(updated.getResult(), propagationReporter.getStatuses()));
+                exchange.getOut().setBody(Pair.of(updated.getResult(), reporter.getStatuses()));
             }
         }
     }
-
 }
diff --git a/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/UserRequestLogic.java b/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/UserRequestLogic.java
index d3851cd..cdef7bf 100644
--- a/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/UserRequestLogic.java
+++ b/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/UserRequestLogic.java
@@ -25,7 +25,6 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.patch.UserPatch;
 import org.apache.syncope.common.lib.to.EntityTO;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.to.UserRequest;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.to.UserRequestForm;
@@ -42,6 +41,7 @@ import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 import org.apache.syncope.core.flowable.api.UserRequestHandler;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.flowable.engine.runtime.ProcessInstance;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -195,13 +195,13 @@ public class UserRequestLogic extends AbstractTransactionalLogic<EntityTO> {
         // propByRes can be made empty by the workflow definition if no propagation should occur 
         // (for example, with rejected users)
         if (wfResult.getPropByRes() != null && !wfResult.getPropByRes().isEmpty()) {
-            List<PropagationTaskTO> tasks = propagationManager.getUserUpdateTasks(
+            List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(
                     new WorkflowResult<>(
                             Pair.of(wfResult.getResult(), Boolean.TRUE),
                             wfResult.getPropByRes(),
                             wfResult.getPerformedTasks()));
 
-            taskExecutor.execute(tasks, false);
+            taskExecutor.execute(taskInfos, false);
         }
 
         UserTO userTO;
diff --git a/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/UserWorkflowTaskLogic.java b/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/UserWorkflowTaskLogic.java
index 76d4ad9..20fe4cb 100644
--- a/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/UserWorkflowTaskLogic.java
+++ b/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/UserWorkflowTaskLogic.java
@@ -23,7 +23,6 @@ import java.util.List;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.UserPatch;
 import org.apache.syncope.common.lib.to.EntityTO;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.to.WorkflowTask;
 import org.apache.syncope.common.lib.to.WorkflowTaskExecInput;
@@ -36,6 +35,7 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecu
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 import org.apache.syncope.core.flowable.api.WorkflowTaskManager;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Component;
@@ -72,12 +72,12 @@ public class UserWorkflowTaskLogic extends AbstractTransactionalLogic<EntityTO>
         UserPatch userPatch = new UserPatch();
         userPatch.setKey(updated.getResult());
 
-        List<PropagationTaskTO> tasks = propagationManager.getUserUpdateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(
                 new WorkflowResult<>(
                         Pair.<UserPatch, Boolean>of(userPatch, null),
                         updated.getPropByRes(), updated.getPerformedTasks()));
 
-        taskExecutor.execute(tasks, false);
+        taskExecutor.execute(taskInfos, false);
 
         return binder.getUserTO(updated.getResult());
     }