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 2020/01/09 10:46:19 UTC

[syncope] branch master updated (f3314bb -> 21ac766)

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

ilgrosso pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git.


    from f3314bb  Upgrading Wicket
     new 7adde17  More Unit Tests for provisioning-java (#155)
     new 18d84af  [SYNCOPE-1531] Improvements: Swagger docs + propagation actions for CSV export + result page in Admin Console for CSV import
     new 21ac766  Downgrading Wicket version because of misbehavior of new FormComponent#getParameterValues with MockWebRequest

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 ...AnyDirectoryPanelAdditionalActionsProvider.java |  42 +++++--
 .../client/console/panels/CSVConfPanel.java        |   7 +-
 .../console/rest/ReconciliationRestClient.java     |   8 +-
 .../console/wizards/CSVPullWizardBuilder.java      |  21 ++--
 .../console/wizards/CSVPushWizardBuilder.java      |  28 ++++-
 .../wizards/any/LinkedAccountPlainAttrsPanel.java  |   3 +-
 .../wizards/any/ProvisioningReportsPanel.java      |  95 +++++++++++++++
 .../wizards/CSVPullWizardBuilder$PullTask.html     |   2 +-
 .../CSVPullWizardBuilder$PullTask.properties       |   2 +-
 .../CSVPullWizardBuilder$PullTask_fr_CA.properties |   2 +-
 .../CSVPullWizardBuilder$PullTask_it.properties    |   2 +-
 .../CSVPullWizardBuilder$PullTask_ja.properties    |   2 +-
 .../CSVPullWizardBuilder$PullTask_pt_BR.properties |   2 +-
 .../CSVPullWizardBuilder$PullTask_ru.properties    |   2 +-
 .../wizards/CSVPushWizardBuilder$PushTask.html     |   3 +-
 .../CSVPushWizardBuilder$PushTask.properties       |   3 +-
 .../CSVPushWizardBuilder$PushTask_fr_CA.properties |   3 +-
 .../CSVPushWizardBuilder$PushTask_it.properties    |   3 +-
 .../CSVPushWizardBuilder$PushTask_ja.properties    |   3 +-
 .../CSVPushWizardBuilder$PushTask_pt_BR.properties |   3 +-
 .../CSVPushWizardBuilder$PushTask_ru.properties    |   3 +-
 .../any/ProvisioningReportsPanel.html}             |   4 +-
 .../any/ProvisioningReportsPanel.properties}       |   5 +-
 .../any/ProvisioningReportsPanel_fr_CA.properties  |   7 +-
 .../any/ProvisioningReportsPanel_it.properties}    |   5 +-
 .../any/ProvisioningReportsPanel_ja.properties}    |   5 +-
 .../any/ProvisioningReportsPanel_pt_BR.properties} |   5 +-
 .../any/ProvisioningReportsPanel_ru.properties}    |   5 +-
 .../syncope/client/ui/commons/BaseLogin.java       |   3 +-
 .../console/audit/AuditHistoryDirectoryPanel.java  |   3 +-
 .../client/console/panels/AnyDirectoryPanel.java   |   3 +-
 .../client/console/panels/ConsoleLogPanel.java     |   6 +-
 .../markup/html/form/AjaxCharacterFieldPanel.java  |   3 +-
 .../wizards/any/AbstractAttrsWizardStep.java       |   5 +-
 .../syncope/common/lib/to/ProvisioningReport.java  |  15 ++-
 .../common/rest/api/beans/AbstractCSVSpec.java     |  70 ++++++++---
 .../syncope/common/rest/api/beans/CSVPushSpec.java |  16 +++
 .../syncope/common/rest/api/beans/ReconQuery.java  |   3 +-
 .../syncope/common/rest/api/beans/ExecQuery.java   |   1 -
 .../common/rest/api/beans/ExecuteQuery.java        |   1 -
 .../common/rest/api/service/JAXRSService.java      |   2 +
 .../syncope/core/logic/ReconciliationLogic.java    |  69 +++++------
 .../core/logic/ReconciliationLogicTest.java        |  12 +-
 .../api/jexl/SyncopeJexlFunctions.java             |   1 -
 .../pushpull/stream/SyncopeStreamPullExecutor.java |   3 +-
 .../pushpull/stream/SyncopeStreamPushExecutor.java |   4 +-
 .../provisioning/api/IntAttrNameParserTest.java    |   7 +-
 core/provisioning-java/pom.xml                     |   5 +
 .../java/pushpull/OutboundMatcher.java             |   5 +-
 .../java/pushpull/stream/CSVStreamConnector.java}  | 132 +++++++++++++++------
 .../pushpull/stream/StreamPullJobDelegate.java     |   4 +-
 .../pushpull/stream/StreamPushJobDelegate.java     |  30 +++--
 .../core/provisioning/java/utils/MappingUtils.java |   3 +-
 .../java/pushpull/LDAPPasswordPullActionsTest.java | 131 ++++++++++++++++++++
 .../pushpull/stream/StreamPullJobDelegateTest.java |  58 +++++----
 .../pushpull/stream/StreamPushJobDelegateTest.java |  36 +++---
 core/spring/pom.xml                                |   9 +-
 .../client/console/rest/BpmnProcessRestClient.java |   5 +-
 .../syncope/core/logic/saml2/SAML2UserManager.java |   3 +-
 .../org/apache/syncope/fit/console/LogsITCase.java |   5 +-
 .../syncope/fit/core/ReconciliationITCase.java     |  13 +-
 pom.xml                                            |   2 +-
 62 files changed, 682 insertions(+), 261 deletions(-)
 create mode 100644 client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel.java
 copy client/idm/console/src/main/resources/org/apache/syncope/client/console/{panels/ConnObjectDetails.html => wizards/any/ProvisioningReportsPanel.html} (90%)
 copy client/idm/console/src/main/resources/org/apache/syncope/client/console/{panels/ConnObjectListViewPanel_pt_BR.properties => wizards/any/ProvisioningReportsPanel.properties} (87%)
 copy archetype/src/main/resources/archetype-resources/sra/src/test/resources/keymaster.properties => client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel_fr_CA.properties (84%)
 copy client/idm/console/src/main/resources/org/apache/syncope/client/console/{panels/ConnObjectListViewPanel_pt_BR.properties => wizards/any/ProvisioningReportsPanel_it.properties} (85%)
 copy client/idm/console/src/main/resources/org/apache/syncope/client/console/{panels/ConnObjectListViewPanel_pt_BR.properties => wizards/any/ProvisioningReportsPanel_ja.properties} (87%)
 copy client/idm/console/src/main/resources/org/apache/syncope/client/console/{panels/ConnObjectListViewPanel_pt_BR.properties => wizards/any/ProvisioningReportsPanel_pt_BR.properties} (87%)
 copy client/idm/console/src/main/resources/org/apache/syncope/client/console/{panels/ConnObjectListViewPanel_pt_BR.properties => wizards/any/ProvisioningReportsPanel_ru.properties} (87%)
 rename common/{idrepo => idm}/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningReport.java (90%)
 rename core/{provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/StreamConnector.java => provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/CSVStreamConnector.java} (60%)
 create mode 100644 core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActionsTest.java


[syncope] 01/03: More Unit Tests for provisioning-java (#155)

Posted by il...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 7adde17fdc741a81a44506c08ffcd0274c5c3a23
Author: Davide Cortellucci <da...@gmail.com>
AuthorDate: Wed Jan 8 14:30:02 2020 +0100

    More Unit Tests for provisioning-java (#155)
---
 .../java/pushpull/LDAPPasswordPullActionsTest.java | 130 +++++++++++++++++++++
 1 file changed, 130 insertions(+)

diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActionsTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActionsTest.java
new file mode 100644
index 0000000..de4a6bb
--- /dev/null
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActionsTest.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.pushpull;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import org.apache.syncope.common.lib.patch.AnyPatch;
+import org.apache.syncope.common.lib.patch.PasswordPatch;
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.EntityTO;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.CipherAlgorithm;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
+import org.apache.syncope.core.provisioning.java.AbstractTest;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.quartz.JobExecutionException;
+import org.springframework.test.util.ReflectionTestUtils;
+
+public class LDAPPasswordPullActionsTest extends AbstractTest {
+
+    @Mock
+    private SyncDelta syncDelta;
+
+    @Mock
+    private ProvisioningProfile<?, ?> profile;
+
+    @Mock
+    private AnyPatch anyPatch;
+
+    @Mock
+    private UserDAO userDAO;
+
+    @Mock
+    private ProvisioningReport result;
+
+    @InjectMocks
+    private LDAPPasswordPullActions ldapPasswordPullActions;
+
+    private EntityTO entity;
+
+    private String encodedPassword;
+
+    private CipherAlgorithm cipher;
+
+    @BeforeEach
+    public void initTest() {
+        entity = new UserTO();
+        encodedPassword = "s3cureP4ssw0rd";
+        cipher = CipherAlgorithm.SHA512;
+
+        ReflectionTestUtils.setField(ldapPasswordPullActions, "encodedPassword", encodedPassword);
+        ReflectionTestUtils.setField(ldapPasswordPullActions, "cipher", cipher);
+    }
+
+    @Test
+    public void beforeProvision() throws JobExecutionException {
+        String digest = "SHA256";
+        String password = "t3stPassw0rd";
+        ReflectionTestUtils.setField(entity, "password", String.format("{%s}%s", digest, password));
+
+        ldapPasswordPullActions.beforeProvision(profile, syncDelta, entity);
+
+        assertEquals(CipherAlgorithm.valueOf(digest), ReflectionTestUtils.getField(ldapPasswordPullActions, "cipher"));
+        assertEquals(password, ReflectionTestUtils.getField(ldapPasswordPullActions, "encodedPassword"));
+    }
+
+    @Test
+    public void beforeUpdate() throws JobExecutionException {
+        anyPatch = new UserPatch();
+        PasswordPatch passwordPatch = new PasswordPatch();
+        String digest = "MD5";
+        String password = "an0therTestP4ss";
+        ReflectionTestUtils.setField(passwordPatch, "value", String.format("{%s}%s", digest, password));
+        ReflectionTestUtils.setField(anyPatch, "password", passwordPatch);
+
+        ldapPasswordPullActions.beforeUpdate(profile, syncDelta, entity, anyPatch);
+
+        assertNull(ReflectionTestUtils.getField(ldapPasswordPullActions, "encodedPassword"));
+    }
+
+    @Test
+    public void afterWithNullUser() throws JobExecutionException {
+        when(userDAO.find(entity.getKey())).thenReturn(null);
+
+        ldapPasswordPullActions.after(profile, syncDelta, entity, result);
+
+        assertNull(ReflectionTestUtils.getField(ldapPasswordPullActions, "encodedPassword"));
+        assertNull(ReflectionTestUtils.getField(ldapPasswordPullActions, "cipher"));
+    }
+
+    @Test
+    public void after(@Mock User user) throws JobExecutionException {
+        when(userDAO.find(entity.getKey())).thenReturn(user);
+
+        ldapPasswordPullActions.after(profile, syncDelta, entity, result);
+
+        verify(user).setEncodedPassword(anyString(), any(CipherAlgorithm.class));
+        assertNull(ReflectionTestUtils.getField(ldapPasswordPullActions, "encodedPassword"));
+        assertNull(ReflectionTestUtils.getField(ldapPasswordPullActions, "cipher"));
+    }
+
+}


[syncope] 03/03: Downgrading Wicket version because of misbehavior of new FormComponent#getParameterValues with MockWebRequest

Posted by il...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 21ac766cdd2dfa67babee9280cac9dae7f4647ac
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Thu Jan 9 11:46:01 2020 +0100

    Downgrading Wicket version because of misbehavior of new FormComponent#getParameterValues with MockWebRequest
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index e50b936..9d7c545 100644
--- a/pom.xml
+++ b/pom.xml
@@ -481,7 +481,7 @@ under the License.
     <jsplumb.version>2.0.7</jsplumb.version>
     <chartjs.version>1.0.2</chartjs.version>
     
-    <wicket.version>8.7.0</wicket.version>
+    <wicket.version>8.6.0</wicket.version>
     <wicket-jqueryui.version>8.6.0</wicket-jqueryui.version>
     <wicket-bootstrap.version>2.0.11</wicket-bootstrap.version>
     <wicket-spring-boot.version>2.1.8</wicket-spring-boot.version>


[syncope] 02/03: [SYNCOPE-1531] Improvements: Swagger docs + propagation actions for CSV export + result page in Admin Console for CSV import

Posted by il...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 18d84af51dee57f4636ddca65dcdf33b4b7e29f5
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Wed Jan 8 17:17:38 2020 +0100

    [SYNCOPE-1531] Improvements: Swagger docs + propagation actions for CSV export + result page in Admin Console for CSV import
---
 ...AnyDirectoryPanelAdditionalActionsProvider.java |  42 +++++--
 .../client/console/panels/CSVConfPanel.java        |   7 +-
 .../console/rest/ReconciliationRestClient.java     |   8 +-
 .../console/wizards/CSVPullWizardBuilder.java      |  21 ++--
 .../console/wizards/CSVPushWizardBuilder.java      |  28 ++++-
 .../wizards/any/LinkedAccountPlainAttrsPanel.java  |   3 +-
 .../wizards/any/ProvisioningReportsPanel.java      |  95 +++++++++++++++
 .../wizards/CSVPullWizardBuilder$PullTask.html     |   2 +-
 .../CSVPullWizardBuilder$PullTask.properties       |   2 +-
 .../CSVPullWizardBuilder$PullTask_fr_CA.properties |   2 +-
 .../CSVPullWizardBuilder$PullTask_it.properties    |   2 +-
 .../CSVPullWizardBuilder$PullTask_ja.properties    |   2 +-
 .../CSVPullWizardBuilder$PullTask_pt_BR.properties |   2 +-
 .../CSVPullWizardBuilder$PullTask_ru.properties    |   2 +-
 .../wizards/CSVPushWizardBuilder$PushTask.html     |   3 +-
 .../CSVPushWizardBuilder$PushTask.properties       |   3 +-
 .../CSVPushWizardBuilder$PushTask_fr_CA.properties |   3 +-
 .../CSVPushWizardBuilder$PushTask_it.properties    |   3 +-
 .../CSVPushWizardBuilder$PushTask_ja.properties    |   3 +-
 .../CSVPushWizardBuilder$PushTask_pt_BR.properties |   3 +-
 .../CSVPushWizardBuilder$PushTask_ru.properties    |   3 +-
 .../ProvisioningReportsPanel.html}                 |  11 +-
 .../ProvisioningReportsPanel.properties}           |   8 +-
 .../ProvisioningReportsPanel_fr_CA.properties}     |   8 +-
 .../ProvisioningReportsPanel_it.properties}        |   8 +-
 .../ProvisioningReportsPanel_ja.properties}        |   8 +-
 .../ProvisioningReportsPanel_pt_BR.properties}     |   8 +-
 .../ProvisioningReportsPanel_ru.properties}        |   8 +-
 .../syncope/client/ui/commons/BaseLogin.java       |   3 +-
 .../console/audit/AuditHistoryDirectoryPanel.java  |   3 +-
 .../client/console/panels/AnyDirectoryPanel.java   |   3 +-
 .../client/console/panels/ConsoleLogPanel.java     |   6 +-
 .../markup/html/form/AjaxCharacterFieldPanel.java  |   3 +-
 .../wizards/any/AbstractAttrsWizardStep.java       |   5 +-
 .../syncope/common/lib/to/ProvisioningReport.java  |  15 ++-
 .../common/rest/api/beans/AbstractCSVSpec.java     |  70 ++++++++---
 .../syncope/common/rest/api/beans/CSVPushSpec.java |  16 +++
 .../syncope/common/rest/api/beans/ReconQuery.java  |   3 +-
 .../syncope/common/rest/api/beans/ExecQuery.java   |   1 -
 .../common/rest/api/beans/ExecuteQuery.java        |   1 -
 .../common/rest/api/service/JAXRSService.java      |   2 +
 .../syncope/core/logic/ReconciliationLogic.java    |  69 +++++------
 .../core/logic/ReconciliationLogicTest.java        |  12 +-
 .../api/jexl/SyncopeJexlFunctions.java             |   1 -
 .../pushpull/stream/SyncopeStreamPullExecutor.java |   3 +-
 .../pushpull/stream/SyncopeStreamPushExecutor.java |   4 +-
 .../provisioning/api/IntAttrNameParserTest.java    |   7 +-
 core/provisioning-java/pom.xml                     |   5 +
 .../java/pushpull/OutboundMatcher.java             |   5 +-
 .../java/pushpull/stream/CSVStreamConnector.java}  | 132 +++++++++++++++------
 .../pushpull/stream/StreamPullJobDelegate.java     |   4 +-
 .../pushpull/stream/StreamPushJobDelegate.java     |  30 +++--
 .../core/provisioning/java/utils/MappingUtils.java |   3 +-
 .../java/pushpull/LDAPPasswordPullActionsTest.java |  33 +++---
 .../pushpull/stream/StreamPullJobDelegateTest.java |  58 +++++----
 .../pushpull/stream/StreamPushJobDelegateTest.java |  36 +++---
 core/spring/pom.xml                                |   9 +-
 .../client/console/rest/BpmnProcessRestClient.java |   5 +-
 .../syncope/core/logic/saml2/SAML2UserManager.java |   3 +-
 .../org/apache/syncope/fit/console/LogsITCase.java |   5 +-
 .../syncope/fit/core/ReconciliationITCase.java     |  13 +-
 61 files changed, 567 insertions(+), 299 deletions(-)

diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMAnyDirectoryPanelAdditionalActionsProvider.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMAnyDirectoryPanelAdditionalActionsProvider.java
index c6abd9d..b091655 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMAnyDirectoryPanelAdditionalActionsProvider.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMAnyDirectoryPanelAdditionalActionsProvider.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.client.console.commons;
 
+import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.syncope.client.console.PreferenceManager;
@@ -29,8 +31,11 @@ import org.apache.syncope.client.console.wicket.ajax.form.AjaxDownloadBehavior;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
 import org.apache.syncope.client.console.wizards.CSVPullWizardBuilder;
 import org.apache.syncope.client.console.wizards.CSVPushWizardBuilder;
+import org.apache.syncope.client.console.wizards.any.ProvisioningReportsPanel;
+import org.apache.syncope.client.console.wizards.any.ResultPage;
 import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
 import org.apache.syncope.common.rest.api.beans.AnyQuery;
 import org.apache.syncope.common.rest.api.beans.CSVPullSpec;
 import org.apache.syncope.common.rest.api.beans.CSVPushSpec;
@@ -39,6 +44,7 @@ import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.markup.html.AjaxLink;
 import org.apache.wicket.event.IEvent;
 import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.StringResourceModel;
 
@@ -70,17 +76,39 @@ public class IdMAnyDirectoryPanelAdditionalActionsProvider implements AnyDirecto
                     modal.close(target);
                 } else if (event.getPayload() instanceof AjaxWizard.NewItemFinishEvent) {
                     AjaxWizard.NewItemFinishEvent<?> payload = (AjaxWizard.NewItemFinishEvent) event.getPayload();
-                    if (Constants.OPERATION_SUCCEEDED.equals(payload.getResult())) {
-                        AjaxRequestTarget target = payload.getTarget();
+                    AjaxRequestTarget target = payload.getTarget();
 
+                    SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                    ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+
+                    target.add(container);
+
+                    if (payload.getResult() instanceof ArrayList) {
+                        modal.setContent(new ResultPage<Serializable>(
+                                null,
+                                payload.getResult()) {
+
+                            private static final long serialVersionUID = -2630573849050255233L;
+
+                            @Override
+                            protected void closeAction(final AjaxRequestTarget target) {
+                                modal.close(target);
+                            }
+
+                            @Override
+                            protected Panel customResultBody(
+                                    final String id, final Serializable item, final Serializable result) {
+
+                                @SuppressWarnings("unchecked")
+                                ArrayList<ProvisioningReport> reports = (ArrayList<ProvisioningReport>) result;
+                                return new ProvisioningReportsPanel(id, reports, pageRef);
+                            }
+                        });
+                        target.add(modal.getForm());
+                    } else if (Constants.OPERATION_SUCCEEDED.equals(payload.getResult())) {
                         if (csvDownloadBehavior.hasResponse()) {
                             csvDownloadBehavior.initiate(target);
                         }
-
-                        SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
-                        ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
-
-                        target.add(container);
                         modal.close(target);
                     }
                 }
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/CSVConfPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/CSVConfPanel.java
index a5e4f0c..4867673 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/CSVConfPanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/CSVConfPanel.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.client.console.panels;
 
-import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -43,21 +42,21 @@ public class CSVConfPanel extends Panel {
 
         AjaxCharacterFieldPanel columnSeparator = new AjaxCharacterFieldPanel(
                 "columnSeparator", "columnSeparator", new PropertyModel<>(spec, "columnSeparator"));
-        columnSeparator.setChoices(Arrays.asList(',', ';', ' '));
+        columnSeparator.setChoices(List.of(',', ';', ' '));
         columnSeparator.setRequired(true);
         add(columnSeparator);
 
         AjaxTextFieldPanel arrayElementSeparator = new AjaxTextFieldPanel(
                 "arrayElementSeparator", "arrayElementSeparator",
                 new PropertyModel<>(spec, "arrayElementSeparator"));
-        arrayElementSeparator.setChoices(Arrays.asList(";"));
+        arrayElementSeparator.setChoices(List.of(";"));
         arrayElementSeparator.setRequired(true);
         arrayElementSeparator.addValidator(new StringValidator(1, 1));
         add(arrayElementSeparator);
 
         AjaxCharacterFieldPanel quoteChar = new AjaxCharacterFieldPanel(
                 "quoteChar", "quoteChar", new PropertyModel<>(spec, "quoteChar"));
-        quoteChar.setChoices(Arrays.asList('"'));
+        quoteChar.setChoices(List.of('"'));
         quoteChar.setRequired(true);
         add(quoteChar);
 
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ReconciliationRestClient.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ReconciliationRestClient.java
index 3855eb7..a341569 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ReconciliationRestClient.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ReconciliationRestClient.java
@@ -19,7 +19,8 @@
 package org.apache.syncope.client.console.rest;
 
 import java.io.InputStream;
-import java.util.List;
+import java.util.ArrayList;
+import java.util.stream.Collectors;
 import javax.ws.rs.core.Response;
 import org.apache.cxf.jaxrs.client.Client;
 import org.apache.cxf.jaxrs.client.WebClient;
@@ -63,12 +64,13 @@ public class ReconciliationRestClient extends BaseRestClient {
         return response;
     }
 
-    public static List<ProvisioningReport> pull(final CSVPullSpec spec, final InputStream csv) {
+    public static ArrayList<ProvisioningReport> pull(final CSVPullSpec spec, final InputStream csv) {
         ReconciliationService service = getService(ReconciliationService.class);
         Client client = WebClient.client(service);
         client.type(RESTHeaders.TEXT_CSV);
 
-        List<ProvisioningReport> result = service.pull(spec, csv);
+        ArrayList<ProvisioningReport> result = service.pull(spec, csv).stream().
+                collect(Collectors.toCollection(ArrayList::new));
 
         SyncopeConsoleSession.get().resetClient(ReconciliationService.class);
 
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder.java
index 1c3a2a2..99fb6b1 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder.java
@@ -21,9 +21,7 @@ package org.apache.syncope.client.console.wizards;
 import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.fileinput.BootstrapFileInputField;
 import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.fileinput.FileInputConfig;
 import java.io.ByteArrayInputStream;
-import java.io.Serializable;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -36,6 +34,7 @@ import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
 import org.apache.syncope.common.lib.to.EntityTO;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
 import org.apache.syncope.common.lib.types.ConflictResolutionAction;
 import org.apache.syncope.common.lib.types.IdMImplementationType;
 import org.apache.syncope.common.lib.types.MatchingRule;
@@ -102,9 +101,8 @@ public class CSVPullWizardBuilder extends BaseAjaxWizardBuilder<CSVPullSpec> {
     }
 
     @Override
-    protected Serializable onApplyInternal(final CSVPullSpec modelObject) {
-        ReconciliationRestClient.pull(modelObject, new ByteArrayInputStream(csv.getObject()));
-        return Constants.OPERATION_SUCCEEDED;
+    protected ArrayList<ProvisioningReport> onApplyInternal(final CSVPullSpec modelObject) {
+        return ReconciliationRestClient.pull(modelObject, new ByteArrayInputStream(csv.getObject()));
     }
 
     @Override
@@ -243,23 +241,24 @@ public class CSVPullWizardBuilder extends BaseAjaxWizardBuilder<CSVPullSpec> {
 
             AjaxDropDownChoicePanel<MatchingRule> matchingRule = new AjaxDropDownChoicePanel<>(
                     "matchingRule", "matchingRule", new PropertyModel<>(spec, "matchingRule"), false);
-            matchingRule.setChoices(Arrays.asList(MatchingRule.values()));
+            matchingRule.setChoices(List.of(MatchingRule.values()));
             add(matchingRule);
 
             AjaxDropDownChoicePanel<UnmatchingRule> unmatchingRule = new AjaxDropDownChoicePanel<>(
                     "unmatchingRule", "unmatchingRule", new PropertyModel<>(spec, "unmatchingRule"),
                     false);
-            unmatchingRule.setChoices(Arrays.asList(UnmatchingRule.values()));
+            unmatchingRule.setChoices(List.of(UnmatchingRule.values()));
             add(unmatchingRule);
 
-            AjaxPalettePanel<String> actions = new AjaxPalettePanel.Builder<String>().
-                    build("actions", new PropertyModel<>(spec, "actions"), new ListModel<>(pullActions.getObject()));
-            add(actions);
+            AjaxPalettePanel<String> provisioningActions =
+                    new AjaxPalettePanel.Builder<String>().build("provisioningActions",
+                            new PropertyModel<>(spec, "provisioningActions"), new ListModel<>(pullActions.getObject()));
+            add(provisioningActions);
 
             AjaxDropDownChoicePanel<ConflictResolutionAction> conflictResolutionAction = new AjaxDropDownChoicePanel<>(
                     "conflictResolutionAction", "conflictResolutionAction",
                     new PropertyModel<>(spec, "conflictResolutionAction"), false);
-            conflictResolutionAction.setChoices(Arrays.asList(ConflictResolutionAction.values()));
+            conflictResolutionAction.setChoices(List.of(ConflictResolutionAction.values()));
             conflictResolutionAction.setRequired(true);
             add(conflictResolutionAction);
 
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder.java
index 1f2911d..ee2af97 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder.java
@@ -19,7 +19,6 @@
 package org.apache.syncope.client.console.wizards;
 
 import java.io.Serializable;
-import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.syncope.client.console.panels.CSVConfPanel;
@@ -98,6 +97,17 @@ public class CSVPushWizardBuilder extends BaseAjaxWizardBuilder<CSVPushSpec> {
 
         private final ImplementationRestClient implRestClient = new ImplementationRestClient();
 
+        private final IModel<List<String>> propActions = new LoadableDetachableModel<List<String>>() {
+
+            private static final long serialVersionUID = 4659376149825914247L;
+
+            @Override
+            protected List<String> load() {
+                return implRestClient.list(IdMImplementationType.PROPAGATION_ACTIONS).stream().
+                        map(EntityTO::getKey).sorted().collect(Collectors.toList());
+            }
+        };
+
         private final IModel<List<String>> pushActions = new LoadableDetachableModel<List<String>>() {
 
             private static final long serialVersionUID = 4659376149825914247L;
@@ -114,20 +124,26 @@ public class CSVPushWizardBuilder extends BaseAjaxWizardBuilder<CSVPushSpec> {
                     "ignorePaging", "ignorePaging", new PropertyModel<>(spec, "ignorePaging"), true);
             add(ignorePaging);
 
+            AjaxPalettePanel<String> propagationActions =
+                    new AjaxPalettePanel.Builder<String>().build("propagationActions",
+                            new PropertyModel<>(spec, "propagationActions"), new ListModel<>(propActions.getObject()));
+            add(propagationActions);
+
             AjaxDropDownChoicePanel<MatchingRule> matchingRule = new AjaxDropDownChoicePanel<>(
                     "matchingRule", "matchingRule", new PropertyModel<>(spec, "matchingRule"), false);
-            matchingRule.setChoices(Arrays.asList(MatchingRule.values()));
+            matchingRule.setChoices(List.of(MatchingRule.values()));
             add(matchingRule);
 
             AjaxDropDownChoicePanel<UnmatchingRule> unmatchingRule = new AjaxDropDownChoicePanel<>(
                     "unmatchingRule", "unmatchingRule", new PropertyModel<>(spec, "unmatchingRule"),
                     false);
-            unmatchingRule.setChoices(Arrays.asList(UnmatchingRule.values()));
+            unmatchingRule.setChoices(List.of(UnmatchingRule.values()));
             add(unmatchingRule);
 
-            AjaxPalettePanel<String> actions = new AjaxPalettePanel.Builder<String>().
-                    build("actions", new PropertyModel<>(spec, "actions"), new ListModel<>(pushActions.getObject()));
-            add(actions);
+            AjaxPalettePanel<String> provisioningActions =
+                    new AjaxPalettePanel.Builder<String>().build("provisioningActions",
+                            new PropertyModel<>(spec, "provisioningActions"), new ListModel<>(pushActions.getObject()));
+            add(provisioningActions);
         }
     }
 }
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountPlainAttrsPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountPlainAttrsPanel.java
index edfbed7..3346de8 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountPlainAttrsPanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountPlainAttrsPanel.java
@@ -22,7 +22,6 @@ import org.apache.syncope.client.ui.commons.wizards.any.EntityWrapper;
 import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.checkbox.bootstraptoggle.BootstrapToggle;
 import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.checkbox.bootstraptoggle.BootstrapToggleConfig;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -80,7 +79,7 @@ public class LinkedAccountPlainAttrsPanel extends AbstractAttrsWizardStep<PlainS
         super(userTO,
                 AjaxWizard.Mode.EDIT,
                 new AnyTypeRestClient().read(userTO.getType()).getClasses(),
-                AnyLayoutUtils.fetch(Arrays.asList(userTO.getType())).getUser().getWhichPlainAttrs(),
+                AnyLayoutUtils.fetch(List.of(userTO.getType())).getUser().getWhichPlainAttrs(),
                 modelObject);
 
         this.linkedAccountTO = modelObject.getInnerObject();
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel.java
new file mode 100644
index 0000000..264bb3f
--- /dev/null
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.wizards.any;
+
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.syncope.client.console.panels.ListViewPanel;
+import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.Model;
+
+public class ProvisioningReportsPanel extends Panel {
+
+    private static final long serialVersionUID = -1450755344104125918L;
+
+    public ProvisioningReportsPanel(
+            final String id, final List<ProvisioningReport> results, final PageReference pageRef) {
+        super(id);
+
+        List<ProvisioningReport> success = results.stream().
+                filter(result -> result.getStatus() == ProvisioningReport.Status.SUCCESS).
+                collect(Collectors.toList());
+        add(new Accordion("success", List.of(new AbstractTab(
+                new Model<>(MessageFormat.format(getString("success"), success.size()))) {
+
+            private static final long serialVersionUID = 1037272333056449378L;
+
+            @Override
+            public WebMarkupContainer getPanel(final String panelId) {
+                return new ListViewPanel.Builder<>(ProvisioningReport.class, pageRef).
+                        setItems(success).
+                        withChecks(ListViewPanel.CheckAvailability.NONE).
+                        includes("name", "message").
+                        build(panelId);
+            }
+        }), Model.of(-1)).setOutputMarkupId(true));
+
+        List<ProvisioningReport> failure = results.stream().
+                filter(result -> result.getStatus() == ProvisioningReport.Status.FAILURE).
+                collect(Collectors.toList());
+        add(new Accordion("failure", List.of(new AbstractTab(
+                new Model<>(MessageFormat.format(getString("failure"), failure.size()))) {
+
+            private static final long serialVersionUID = 1037272333056449378L;
+
+            @Override
+            public WebMarkupContainer getPanel(final String panelId) {
+                return new ListViewPanel.Builder<>(ProvisioningReport.class, pageRef).
+                        setItems(failure).
+                        withChecks(ListViewPanel.CheckAvailability.NONE).
+                        includes("name", "message").
+                        build(panelId);
+            }
+        }), Model.of(-1)).setOutputMarkupId(true));
+
+        List<ProvisioningReport> ignore = results.stream().
+                filter(result -> result.getStatus() == ProvisioningReport.Status.IGNORE).
+                collect(Collectors.toList());
+        add(new Accordion("ignore", List.of(new AbstractTab(
+                new Model<>(MessageFormat.format(getString("ignore"), ignore.size()))) {
+
+            private static final long serialVersionUID = 1037272333056449378L;
+
+            @Override
+            public WebMarkupContainer getPanel(final String panelId) {
+                return new ListViewPanel.Builder<>(ProvisioningReport.class, pageRef).
+                        setItems(ignore).
+                        withChecks(ListViewPanel.CheckAvailability.NONE).
+                        includes("name", "message").
+                        build(panelId);
+            }
+        }), Model.of(-1)).setOutputMarkupId(true));
+    }
+}
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask.html b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask.html
index 761b1f4..aab535e 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask.html
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask.html
@@ -22,7 +22,7 @@ under the License.
       <div wicket:id="remediation">[remediation]</div>
       <div wicket:id="matchingRule">[matchingRule]</div>
       <div wicket:id="unmatchingRule">[unmatchingRule]</div>
-      <div wicket:id="actions">[actions]</div>
+      <div wicket:id="provisioningActions">[provisioningActions]</div>
     </div>
     <div class="form-group">
       <div wicket:id="conflictResolutionAction">[conflictResolutionAction]</div>
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask.properties
index 5527723..eed190d 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask.properties
@@ -17,6 +17,6 @@
 remediation=Remediation
 matchingRule=Matching rule
 unmatchingRule=Unmatching rule
-actions=Actions
+provisioningActions=Pull Actions
 conflictResolutionAction=Conflict resolution action
 pullCorrelationRule=Correlation rule
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_fr_CA.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_fr_CA.properties
index c5e744a..fe255e2 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_fr_CA.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_fr_CA.properties
@@ -17,6 +17,6 @@
 remediation=Remediation
 matchingRule=R\u00e8gle de correspondance
 unmatchingRule=R\u00e8gle in\u00e9gal\u00e9e
-actions=Actions
+provisioningActions=Actions de pull
 conflictResolutionAction=Action de r\u00e9solution de conflits
 pullCorrelationRule=R\u00e8gle de corr\u00e9lation
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_it.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_it.properties
index 02ad161..fddd717 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_it.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_it.properties
@@ -17,6 +17,6 @@
 remediation=Bonifiche
 matchingRule=Regola di matrching
 unmatchingRule=Regola di unmatching
-actions=Azioni
+provisioningActions=Azioni di pull
 conflictResolutionAction=Azione di Risoluzione Conflitti
 pullCorrelationRule=Regola di Correlazione
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_ja.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_ja.properties
index 5527723..eed190d 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_ja.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_ja.properties
@@ -17,6 +17,6 @@
 remediation=Remediation
 matchingRule=Matching rule
 unmatchingRule=Unmatching rule
-actions=Actions
+provisioningActions=Pull Actions
 conflictResolutionAction=Conflict resolution action
 pullCorrelationRule=Correlation rule
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_pt_BR.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_pt_BR.properties
index 5527723..eed190d 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_pt_BR.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_pt_BR.properties
@@ -17,6 +17,6 @@
 remediation=Remediation
 matchingRule=Matching rule
 unmatchingRule=Unmatching rule
-actions=Actions
+provisioningActions=Pull Actions
 conflictResolutionAction=Conflict resolution action
 pullCorrelationRule=Correlation rule
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_ru.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_ru.properties
index 5527723..eed190d 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_ru.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_ru.properties
@@ -17,6 +17,6 @@
 remediation=Remediation
 matchingRule=Matching rule
 unmatchingRule=Unmatching rule
-actions=Actions
+provisioningActions=Pull Actions
 conflictResolutionAction=Conflict resolution action
 pullCorrelationRule=Correlation rule
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.html b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.html
index 3773882..13dab03 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.html
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.html
@@ -20,11 +20,12 @@ under the License.
   <wicket:panel>
     <div class="form-group">
       <div wicket:id="ignorePaging">[ignorePaging]</div>
+      <div wicket:id="propagationActions">[propagationActions]</div>
     </div>
     <div class="form-group">
       <div wicket:id="matchingRule">[matchingRule]</div>
       <div wicket:id="unmatchingRule">[unmatchingRule]</div>
-      <div wicket:id="actions">[actions]</div>
+      <div wicket:id="provisioningActions">[provisioningActions]</div>
     </div>
   </wicket:panel>
 </html>
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties
index f269211..fa1b12b 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties
@@ -16,5 +16,6 @@
 # under the License.
 matchingRule=Matching rule
 unmatchingRule=Unmatching rule
-actions=Actions
+provisioningActions=Push Actions
 ignorePaging=Include all pages?
+propagationActions=Propagation Actions
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_fr_CA.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_fr_CA.properties
index 68e7e24..4ae6771 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_fr_CA.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_fr_CA.properties
@@ -16,5 +16,6 @@
 # under the License.
 matchingRule=R\u00e8gle de correspondance
 unmatchingRule=R\u00e8gle in\u00e9gal\u00e9e
-actions=Actions
+provisioningActions=Actions de push
 ignorePaging=Inclure toutes les pages?
+propagationActions=Actions de propagation
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_it.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_it.properties
index 6dfeb3d..c26816b 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_it.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_it.properties
@@ -16,5 +16,6 @@
 # under the License.
 matchingRule=Regola di matrching
 unmatchingRule=Regola di unmatching
-actions=Azioni
+provisioningActions=Azioni di push
 ignorePaging=Includere tutte le pagine?
+propagationActions=Azioni di propagazione
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_ja.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_ja.properties
index f269211..fa1b12b 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_ja.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_ja.properties
@@ -16,5 +16,6 @@
 # under the License.
 matchingRule=Matching rule
 unmatchingRule=Unmatching rule
-actions=Actions
+provisioningActions=Push Actions
 ignorePaging=Include all pages?
+propagationActions=Propagation Actions
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_pt_BR.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_pt_BR.properties
index f269211..fa1b12b 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_pt_BR.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_pt_BR.properties
@@ -16,5 +16,6 @@
 # under the License.
 matchingRule=Matching rule
 unmatchingRule=Unmatching rule
-actions=Actions
+provisioningActions=Push Actions
 ignorePaging=Include all pages?
+propagationActions=Propagation Actions
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_ru.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_ru.properties
index f269211..fa1b12b 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_ru.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask_ru.properties
@@ -16,5 +16,6 @@
 # under the License.
 matchingRule=Matching rule
 unmatchingRule=Unmatching rule
-actions=Actions
+provisioningActions=Push Actions
 ignorePaging=Include all pages?
+propagationActions=Propagation Actions
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.html b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel.html
similarity index 74%
copy from client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.html
copy to client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel.html
index 3773882..79d6373 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.html
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel.html
@@ -18,13 +18,8 @@ under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
   <wicket:panel>
-    <div class="form-group">
-      <div wicket:id="ignorePaging">[ignorePaging]</div>
-    </div>
-    <div class="form-group">
-      <div wicket:id="matchingRule">[matchingRule]</div>
-      <div wicket:id="unmatchingRule">[unmatchingRule]</div>
-      <div wicket:id="actions">[actions]</div>
-    </div>
+    <div wicket:id="success"/>
+    <div wicket:id="failure"/>
+    <div wicket:id="ignore"/>
   </wicket:panel>
 </html>
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel.properties
similarity index 87%
copy from client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties
copy to client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel.properties
index f269211..709310c 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel.properties
@@ -14,7 +14,7 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-matchingRule=Matching rule
-unmatchingRule=Unmatching rule
-actions=Actions
-ignorePaging=Include all pages?
+success=Successfully imported: {0}
+failure=Failed to import: {0}
+ignore=Ignored during import: {0}
+message=Message
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel_fr_CA.properties
similarity index 84%
copy from client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties
copy to client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel_fr_CA.properties
index f269211..b1800a6 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel_fr_CA.properties
@@ -14,7 +14,7 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-matchingRule=Matching rule
-unmatchingRule=Unmatching rule
-actions=Actions
-ignorePaging=Include all pages?
+success=Import\u00e9 avec succ\u00e8s: {0}
+failure=\u00c9chec de l''importation: {0}
+ignore=Ignor\u00e9 lors de l''importationt: {0}
+message=Message
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel_it.properties
similarity index 85%
copy from client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties
copy to client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel_it.properties
index f269211..2888724 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel_it.properties
@@ -14,7 +14,7 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-matchingRule=Matching rule
-unmatchingRule=Unmatching rule
-actions=Actions
-ignorePaging=Include all pages?
+success=Importati con successo: {0}
+failure=Errori nell''importazione: {0}
+ignore=Ignorati dall''importazione: {0}
+message=Messaggio
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel_ja.properties
similarity index 87%
copy from client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties
copy to client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel_ja.properties
index f269211..709310c 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel_ja.properties
@@ -14,7 +14,7 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-matchingRule=Matching rule
-unmatchingRule=Unmatching rule
-actions=Actions
-ignorePaging=Include all pages?
+success=Successfully imported: {0}
+failure=Failed to import: {0}
+ignore=Ignored during import: {0}
+message=Message
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel_pt_BR.properties
similarity index 87%
copy from client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties
copy to client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel_pt_BR.properties
index f269211..709310c 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel_pt_BR.properties
@@ -14,7 +14,7 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-matchingRule=Matching rule
-unmatchingRule=Unmatching rule
-actions=Actions
-ignorePaging=Include all pages?
+success=Successfully imported: {0}
+failure=Failed to import: {0}
+ignore=Ignored during import: {0}
+message=Message
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel_ru.properties
similarity index 87%
copy from client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties
copy to client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel_ru.properties
index f269211..709310c 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPushWizardBuilder$PushTask.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/any/ProvisioningReportsPanel_ru.properties
@@ -14,7 +14,7 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-matchingRule=Matching rule
-unmatchingRule=Unmatching rule
-actions=Actions
-ignorePaging=Include all pages?
+success=Successfully imported: {0}
+failure=Failed to import: {0}
+ignore=Ignored during import: {0}
+message=Message
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseLogin.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseLogin.java
index 0627544..b177eff 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseLogin.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseLogin.java
@@ -22,7 +22,6 @@ import com.googlecode.wicket.kendo.ui.widget.notification.Notification;
 import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.select.BootstrapSelect;
 import java.security.AccessControlException;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.stream.Collectors;
@@ -260,7 +259,7 @@ public abstract class BaseLogin extends WebPage {
             });
 
             // set default language selection
-            List<Locale> filtered = Collections.emptyList();
+            List<Locale> filtered = List.of();
 
             String acceptLanguage = ((ServletWebRequest) RequestCycle.get().getRequest()).
                     getHeader(HttpHeaders.ACCEPT_LANGUAGE);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDirectoryPanel.java
index cf12f0d..790f0b5 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDirectoryPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDirectoryPanel.java
@@ -19,7 +19,6 @@
 package org.apache.syncope.client.console.audit;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
@@ -54,7 +53,7 @@ public abstract class AuditHistoryDirectoryPanel<T extends EntityTO> extends Dir
 
     private static final long serialVersionUID = -8248734710505211261L;
 
-    private static final List<String> EVENTS = Arrays.asList("create", "update");
+    private static final List<String> EVENTS = List.of("create", "update");
 
     private static final SortParam<String> REST_SORT = new SortParam<>("event_date", false);
 
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AnyDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AnyDirectoryPanel.java
index da28bb2..c662fb7 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AnyDirectoryPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AnyDirectoryPanel.java
@@ -22,7 +22,6 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
 import java.io.Serializable;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Date;
 import java.util.List;
@@ -176,7 +175,7 @@ public abstract class AnyDirectoryPanel<A extends AnyTO, E extends AbstractAnyRe
 
             PreferenceManager.setList(
                     getRequest(), getResponse(), DisplayAttributesModalPanel.getPrefDetailView(type),
-                    Arrays.asList(getDefaultAttributeSelection()));
+                    List.of(getDefaultAttributeSelection()));
         }
 
         columns.addAll(prefcolumns);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ConsoleLogPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ConsoleLogPanel.java
index 1d6f557..1c5c703 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ConsoleLogPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ConsoleLogPanel.java
@@ -34,8 +34,6 @@ public class ConsoleLogPanel extends AbstractLogsPanel<LoggerTO> {
 
     private static final long serialVersionUID = -9165749229623482717L;
 
-    private static final ConsoleLoggerController CONSOLE_LOGGER_CONTROLLER = new ConsoleLoggerController();
-
     public ConsoleLogPanel(final String id, final PageReference pageReference) {
         super(id, pageReference, ConsoleLoggerController.getLoggers());
     }
@@ -69,8 +67,8 @@ public class ConsoleLogPanel extends AbstractLogsPanel<LoggerTO> {
         }
 
         public static void setLogLevel(final String name, final LoggerLevel level) {
-            final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
-            final LoggerConfig logConf = SyncopeConstants.ROOT_LOGGER.equals(name)
+            LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+            LoggerConfig logConf = SyncopeConstants.ROOT_LOGGER.equals(name)
                     ? ctx.getConfiguration().getLoggerConfig(LogManager.ROOT_LOGGER_NAME)
                     : ctx.getConfiguration().getLoggerConfig(name);
             logConf.setLevel(level.getLevel());
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxCharacterFieldPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxCharacterFieldPanel.java
index a880b95..8247a2a 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxCharacterFieldPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxCharacterFieldPanel.java
@@ -19,7 +19,6 @@
 package org.apache.syncope.client.console.wicket.markup.html.form;
 
 import de.agilecoders.wicket.core.markup.html.bootstrap.components.TooltipConfig;
-import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import org.apache.syncope.client.ui.commons.Constants;
@@ -42,7 +41,7 @@ public class AjaxCharacterFieldPanel extends FieldPanel<Character> implements Cl
 
     private Component questionMarkJexlHelp;
 
-    private List<Character> choices = Collections.emptyList();
+    private List<Character> choices = List.of();
 
     public AjaxCharacterFieldPanel(final String id, final String name, final IModel<Character> model) {
         this(id, name, model, true);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java
index 2b131f9..5cd5e69 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java
@@ -20,7 +20,6 @@ package org.apache.syncope.client.console.wizards.any;
 
 import java.io.Serializable;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -104,7 +103,7 @@ public abstract class AbstractAttrsWizardStep<S extends SchemaTO> extends Wizard
 
         super();
         this.anyTypeClasses = anyTypeClasses;
-        this.attrs = new ListModel<>(Collections.emptyList());
+        this.attrs = new ListModel<>(List.of());
 
         this.setOutputMarkupId(true);
 
@@ -134,7 +133,7 @@ public abstract class AbstractAttrsWizardStep<S extends SchemaTO> extends Wizard
 
     protected void setSchemas(final List<String> anyTypeClasses, final Map<String, S> scs) {
         List<S> allSchemas = anyTypeClasses.isEmpty()
-                ? Collections.emptyList()
+                ? List.of()
                 : SchemaRestClient.getSchemas(getSchemaType(), null, anyTypeClasses.toArray(new String[] {}));
 
         scs.clear();
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningReport.java b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningReport.java
similarity index 90%
rename from common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningReport.java
rename to common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningReport.java
index 216ab56..a23745a 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningReport.java
+++ b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningReport.java
@@ -18,14 +18,23 @@
  */
 package org.apache.syncope.common.lib.to;
 
+import java.io.Serializable;
 import java.util.Collection;
+import javax.xml.bind.annotation.XmlEnum;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.common.lib.types.TraceLevel;
 
-public class ProvisioningReport {
+@XmlRootElement(name = "provisioningReport")
+@XmlType
+public class ProvisioningReport implements Serializable {
 
+    private static final long serialVersionUID = 9201119472070963385L;
+
+    @XmlEnum
     public enum Status {
 
         SUCCESS,
@@ -135,9 +144,7 @@ public class ProvisioningReport {
      */
     public static String generate(final Collection<ProvisioningReport> results, final TraceLevel level) {
         StringBuilder sb = new StringBuilder();
-        results.forEach(result -> {
-            sb.append(result.getReportString(level)).append('\n');
-        });
+        results.forEach(result -> sb.append(result.getReportString(level)).append('\n'));
         return sb.toString();
     }
 
diff --git a/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/AbstractCSVSpec.java b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/AbstractCSVSpec.java
index 81ba8f3..192ce44 100644
--- a/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/AbstractCSVSpec.java
+++ b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/AbstractCSVSpec.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.common.rest.api.beans;
 
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Schema;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
@@ -25,11 +27,26 @@ import javax.validation.constraints.NotNull;
 import javax.ws.rs.QueryParam;
 import org.apache.syncope.common.lib.types.MatchingRule;
 import org.apache.syncope.common.lib.types.UnmatchingRule;
+import org.apache.syncope.common.rest.api.service.JAXRSService;
 
 public abstract class AbstractCSVSpec implements Serializable {
 
     private static final long serialVersionUID = 2253975790270165334L;
 
+    private static final String PARAM_COLUMNSEPARATOR = "columnSeparator";
+
+    private static final String PARAM_ARRAYELEMENTSEPARATOR = "arrayElementSeparator";
+
+    private static final String PARAM_QUOTECHAR = "quoteChar";
+
+    private static final String PARAM_ESCAPECHAR = "escapeChar";
+
+    private static final String PARAM_LINESEPARATOR = "lineSeparator";
+
+    private static final String PARAM_NULLVALUE = "nullValue";
+
+    private static final String PARAM_ALLOWCOMMENTS = "allowComments";
+
     protected abstract static class Builder<T extends AbstractCSVSpec, B extends Builder<T, B>> {
 
         protected T instance;
@@ -98,8 +115,8 @@ public abstract class AbstractCSVSpec implements Serializable {
         }
 
         @SuppressWarnings("unchecked")
-        public B action(final String action) {
-            getInstance().getActions().add(action);
+        public B provisioningAction(final String provisioningActions) {
+            getInstance().getProvisioningActions().add(provisioningActions);
             return (B) this;
         }
 
@@ -128,77 +145,98 @@ public abstract class AbstractCSVSpec implements Serializable {
 
     protected MatchingRule matchingRule = MatchingRule.UPDATE;
 
-    protected List<String> actions = new ArrayList<>();
+    protected List<String> provisioningActions = new ArrayList<>();
 
+    @Parameter(name = JAXRSService.PARAM_ANYTYPEKEY, description = "any object type", schema =
+            @Schema(implementation = String.class))
     public String getAnyTypeKey() {
         return anyTypeKey;
     }
 
     @NotNull
-    @QueryParam("anyTypeKey")
+    @QueryParam(JAXRSService.PARAM_ANYTYPEKEY)
     public void setAnyTypeKey(final String anyTypeKey) {
         this.anyTypeKey = anyTypeKey;
     }
 
+    @Parameter(name = PARAM_COLUMNSEPARATOR, description = "separator for column values", schema =
+            @Schema(implementation = char.class, defaultValue = ","))
     public char getColumnSeparator() {
         return columnSeparator;
     }
 
-    @QueryParam("columnSeparator")
+    @QueryParam(PARAM_COLUMNSEPARATOR)
     public void setColumnSeparator(final char columnSeparator) {
         this.columnSeparator = columnSeparator;
     }
 
+    @Parameter(name = PARAM_ARRAYELEMENTSEPARATOR, description = "separator for array elements within a "
+            + "column", schema =
+            @Schema(implementation = String.class, defaultValue = ";"))
     public String getArrayElementSeparator() {
         return arrayElementSeparator;
     }
 
-    @QueryParam("arrayElementSeparator")
+    @QueryParam(PARAM_ARRAYELEMENTSEPARATOR)
     public void setArrayElementSeparator(final String arrayElementSeparator) {
         this.arrayElementSeparator = arrayElementSeparator;
     }
 
+    @Parameter(name = PARAM_QUOTECHAR, description = "character used for quoting values "
+            + "that contain quote characters or linefeeds", schema =
+            @Schema(implementation = char.class, defaultValue = "\""))
     public char getQuoteChar() {
         return quoteChar;
     }
 
-    @QueryParam("quoteChar")
+    @QueryParam(PARAM_QUOTECHAR)
     public void setQuoteChar(final char quoteChar) {
         this.quoteChar = quoteChar;
     }
 
+    @Parameter(name = PARAM_ESCAPECHAR, description = "if any, used to escape values; "
+            + "most commonly defined as backslash", schema =
+            @Schema(implementation = Character.class))
     public Character getEscapeChar() {
         return escapeChar;
     }
 
-    @QueryParam("escapeChar")
+    @QueryParam(PARAM_ESCAPECHAR)
     public void setEscapeChar(final Character escapeChar) {
         this.escapeChar = escapeChar;
     }
 
+    @Parameter(name = PARAM_LINESEPARATOR, description = "character used to separate data rows", schema =
+            @Schema(implementation = String.class, defaultValue = "\\\n"))
     public String getLineSeparator() {
         return lineSeparator;
     }
 
-    @QueryParam("lineSeparator")
+    @QueryParam(PARAM_LINESEPARATOR)
     public void setLineSeparator(final String lineSeparator) {
         this.lineSeparator = lineSeparator;
     }
 
+    @Parameter(name = PARAM_NULLVALUE, description = "when asked to write null, this string value will be used "
+            + "instead", schema =
+            @Schema(implementation = String.class, defaultValue = ""))
     public String getNullValue() {
         return nullValue;
     }
 
-    @QueryParam("nullValue")
+    @QueryParam(PARAM_NULLVALUE)
     public void setNullValue(final String nullValue) {
         this.nullValue = nullValue;
     }
 
+    @Parameter(name = PARAM_ALLOWCOMMENTS, description = "are hash comments, e.g. lines where the first non-whitespace "
+            + "character is '#' allowed? if so, they will be skipped without processing", schema =
+            @Schema(implementation = boolean.class, defaultValue = "false"))
     public boolean isAllowComments() {
         return allowComments;
     }
 
-    @QueryParam("allowComments")
+    @QueryParam(PARAM_ALLOWCOMMENTS)
     public void setAllowComments(final boolean allowComments) {
         this.allowComments = allowComments;
     }
@@ -221,12 +259,12 @@ public abstract class AbstractCSVSpec implements Serializable {
         this.matchingRule = matchingRule;
     }
 
-    public List<String> getActions() {
-        return actions;
+    public List<String> getProvisioningActions() {
+        return provisioningActions;
     }
 
-    @QueryParam("actions")
-    public void setActions(final List<String> actions) {
-        this.actions = actions;
+    @QueryParam("provisioningActions")
+    public void setProvisioningActions(final List<String> provisioningActions) {
+        this.provisioningActions = provisioningActions;
     }
 }
diff --git a/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/CSVPushSpec.java b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/CSVPushSpec.java
index 7663010..0a68810 100644
--- a/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/CSVPushSpec.java
+++ b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/CSVPushSpec.java
@@ -82,6 +82,11 @@ public class CSVPushSpec extends AbstractCSVSpec {
             getInstance().setIgnorePaging(ignorePagination);
             return this;
         }
+
+        public Builder propagationAction(final String propagationAction) {
+            getInstance().getPropagationActions().add(propagationAction);
+            return this;
+        }
     }
 
     private List<String> fields = new ArrayList<>();
@@ -94,6 +99,8 @@ public class CSVPushSpec extends AbstractCSVSpec {
 
     private boolean ignorePaging;
 
+    protected List<String> propagationActions = new ArrayList<>();
+
     public List<String> getFields() {
         return fields;
     }
@@ -138,4 +145,13 @@ public class CSVPushSpec extends AbstractCSVSpec {
     public void setIgnorePaging(final boolean ignorePaging) {
         this.ignorePaging = ignorePaging;
     }
+
+    public List<String> getPropagationActions() {
+        return propagationActions;
+    }
+
+    @QueryParam("propagationActions")
+    public void setPropagationActions(final List<String> propagationActions) {
+        this.propagationActions = propagationActions;
+    }
 }
diff --git a/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ReconQuery.java b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ReconQuery.java
index ddd6799..c4b91d5 100644
--- a/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ReconQuery.java
+++ b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ReconQuery.java
@@ -20,6 +20,7 @@ package org.apache.syncope.common.rest.api.beans;
 
 import javax.validation.constraints.NotNull;
 import javax.ws.rs.QueryParam;
+import org.apache.syncope.common.rest.api.service.JAXRSService;
 
 public class ReconQuery {
 
@@ -61,7 +62,7 @@ public class ReconQuery {
     }
 
     @NotNull
-    @QueryParam("anyTypeKey")
+    @QueryParam(JAXRSService.PARAM_ANYTYPEKEY)
     public void setAnyTypeKey(final String anyTypeKey) {
         this.anyTypeKey = anyTypeKey;
     }
diff --git a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ExecQuery.java b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ExecQuery.java
index 3b735a5..f4de253 100644
--- a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ExecQuery.java
+++ b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ExecQuery.java
@@ -49,5 +49,4 @@ public class ExecQuery extends AbstractQuery {
     public void setKey(final String key) {
         this.key = key;
     }
-
 }
diff --git a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ExecuteQuery.java b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ExecuteQuery.java
index 1947206..2a3ca7f 100644
--- a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ExecuteQuery.java
+++ b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ExecuteQuery.java
@@ -96,5 +96,4 @@ public class ExecuteQuery implements Serializable {
     public void setDryRun(final Boolean dryRun) {
         this.dryRun = dryRun;
     }
-
 }
diff --git a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/JAXRSService.java b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/JAXRSService.java
index 455e208..abcc0e2 100644
--- a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/JAXRSService.java
+++ b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/JAXRSService.java
@@ -46,4 +46,6 @@ public interface JAXRSService {
 
     String PARAM_MAX = "max";
 
+    String PARAM_ANYTYPEKEY = "anyTypeKey";
+
 }
diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
index 388f82b..ccb4380 100644
--- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
+++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
@@ -18,16 +18,12 @@
  */
 package org.apache.syncope.core.logic;
 
-import com.fasterxml.jackson.databind.MappingIterator;
-import com.fasterxml.jackson.databind.SequenceWriter;
-import com.fasterxml.jackson.dataformat.csv.CsvMapper;
 import com.fasterxml.jackson.dataformat.csv.CsvSchema;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import org.apache.commons.lang3.StringUtils;
@@ -73,7 +69,7 @@ import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
-import org.apache.syncope.core.provisioning.api.pushpull.stream.StreamConnector;
+import org.apache.syncope.core.provisioning.java.pushpull.stream.CSVStreamConnector;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopeSinglePullExecutor;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopeSinglePushExecutor;
 import org.apache.syncope.core.provisioning.api.pushpull.stream.SyncopeStreamPullExecutor;
@@ -378,19 +374,18 @@ public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
         }
     }
 
-    private CsvSchema csvSchema(final AbstractCSVSpec spec, final CsvSchema base) {
-        CsvSchema schema = base.
-                withColumnSeparator(spec.getColumnSeparator()).
-                withArrayElementSeparator(spec.getArrayElementSeparator()).
-                withQuoteChar(spec.getQuoteChar()).
-                withLineSeparator(spec.getLineSeparator()).
-                withNullValue(spec.getNullValue()).
-                withAllowComments(spec.isAllowComments());
+    private CsvSchema.Builder csvSchema(final AbstractCSVSpec spec) {
+        CsvSchema.Builder schemaBuilder = new CsvSchema.Builder().setUseHeader(true).
+                setColumnSeparator(spec.getColumnSeparator()).
+                setArrayElementSeparator(spec.getArrayElementSeparator()).
+                setQuoteChar(spec.getQuoteChar()).
+                setLineSeparator(spec.getLineSeparator()).
+                setNullValue(spec.getNullValue()).
+                setAllowComments(spec.isAllowComments());
         if (spec.getEscapeChar() != null) {
-            schema = schema.withEscapeChar(spec.getEscapeChar());
+            schemaBuilder.setEscapeChar(spec.getEscapeChar());
         }
-
-        return schema;
+        return schemaBuilder;
     }
 
     @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_EXECUTE + "')")
@@ -473,21 +468,24 @@ public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
             }
         });
 
-        CsvSchema.Builder schemaBuilder = CsvSchema.builder().setUseHeader(true);
-        columns.forEach(schemaBuilder::addColumn);
-        CsvSchema schema = csvSchema(spec, schemaBuilder.build());
-
         PushTaskTO pushTask = new PushTaskTO();
         pushTask.setMatchingRule(spec.getMatchingRule());
         pushTask.setUnmatchingRule(spec.getUnmatchingRule());
-        pushTask.getActions().addAll(spec.getActions());
+        pushTask.getActions().addAll(spec.getProvisioningActions());
+
+        try (CSVStreamConnector connector = new CSVStreamConnector(
+                null,
+                spec.getArrayElementSeparator(),
+                csvSchema(spec),
+                null,
+                os)) {
 
-        try (SequenceWriter writer = new CsvMapper().writer(schema).forType(Map.class).writeValues(os)) {
             return streamPushExecutor.push(
                     anyType,
                     matching,
                     columns,
-                    new StreamConnector(null, spec.getArrayElementSeparator(), null, writer),
+                    connector,
+                    spec.getPropagationActions(),
                     pushTask,
                     AuthContextUtils.getUsername());
         } catch (Exception e) {
@@ -514,31 +512,26 @@ public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
         pullTask.setRemediation(spec.isRemediation());
         pullTask.setMatchingRule(spec.getMatchingRule());
         pullTask.setUnmatchingRule(spec.getUnmatchingRule());
-        pullTask.getActions().addAll(spec.getActions());
+        pullTask.getActions().addAll(spec.getProvisioningActions());
 
-        CsvSchema schema = csvSchema(spec, CsvSchema.emptySchema().withHeader());
-        try {
-            MappingIterator<Map<String, String>> reader =
-                    new CsvMapper().readerFor(Map.class).with(schema).readValues(csv);
-
-            List<String> columns = new ArrayList<>();
-            ((CsvSchema) reader.getParserSchema()).forEach(column -> {
-                if (!spec.getIgnoreColumns().contains(column.getName())) {
-                    columns.add(column.getName());
-                }
-            });
+        try (CSVStreamConnector connector = new CSVStreamConnector(
+                spec.getKeyColumn(),
+                spec.getArrayElementSeparator(),
+                csvSchema(spec),
+                csv,
+                null)) {
 
+            List<String> columns = connector.getColumns(spec);
             if (!columns.contains(spec.getKeyColumn())) {
                 throw new NotFoundException("Key column '" + spec.getKeyColumn() + "'");
             }
 
-            return streamPullExecutor.pull(
-                    anyType,
+            return streamPullExecutor.pull(anyType,
                     spec.getKeyColumn(),
                     columns,
                     spec.getConflictResolutionAction(),
                     spec.getPullCorrelationRule(),
-                    new StreamConnector(spec.getKeyColumn(), spec.getArrayElementSeparator(), reader, null),
+                    connector,
                     pullTask);
         } catch (NotFoundException e) {
             throw e;
diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ReconciliationLogicTest.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ReconciliationLogicTest.java
index 72d1a93..73064c6 100644
--- a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ReconciliationLogicTest.java
+++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ReconciliationLogicTest.java
@@ -110,16 +110,8 @@ public class ReconciliationLogicTest extends AbstractTest {
         });
         assertEquals(search.getLeft(), results.size());
 
-        CsvSchema.Builder builder = CsvSchema.builder().setUseHeader(true);
-        builder.addColumn("username");
-        builder.addColumn("status");
-        builder.addColumn("firstname");
-        builder.addColumn("surname");
-        builder.addColumn("email");
-        builder.addColumn("loginDate");
-        CsvSchema schema = builder.build();
-
-        MappingIterator<Map<String, String>> reader = new CsvMapper().readerFor(Map.class).with(schema).readValues(in);
+        MappingIterator<Map<String, String>> reader =
+                new CsvMapper().readerFor(Map.class).with(CsvSchema.emptySchema().withHeader()).readValues(in);
 
         for (int i = 0; i < results.size() && reader.hasNext(); i++) {
             Map<String, String> row = reader.next();
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/SyncopeJexlFunctions.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/SyncopeJexlFunctions.java
index 9cc011f..26d695a 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/SyncopeJexlFunctions.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/SyncopeJexlFunctions.java
@@ -65,5 +65,4 @@ public class SyncopeJexlFunctions {
         Collections.reverse(headless);
         return prefix + attr + '=' + StringUtils.join(headless, ',' + attr + '=');
     }
-
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPullExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPullExecutor.java
index 8d5c241..5215e63 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPullExecutor.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPullExecutor.java
@@ -23,6 +23,7 @@ import java.util.List;
 import org.apache.syncope.common.lib.to.PullTaskTO;
 import org.apache.syncope.common.lib.types.ConflictResolutionAction;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
+import org.apache.syncope.core.provisioning.api.Connector;
 import org.quartz.JobExecutionException;
 
 public interface SyncopeStreamPullExecutor {
@@ -33,7 +34,7 @@ public interface SyncopeStreamPullExecutor {
             List<String> columns,
             ConflictResolutionAction conflictResolutionAction,
             String pullCorrelationRule,
-            StreamConnector connector,
+            Connector connector,
             PullTaskTO pullTaskTO)
             throws JobExecutionException;
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPushExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPushExecutor.java
index b70a2c4..c2b1e44 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPushExecutor.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPushExecutor.java
@@ -23,6 +23,7 @@ import org.apache.syncope.common.lib.to.ProvisioningReport;
 import org.apache.syncope.common.lib.to.PushTaskTO;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
+import org.apache.syncope.core.provisioning.api.Connector;
 import org.quartz.JobExecutionException;
 
 public interface SyncopeStreamPushExecutor {
@@ -31,7 +32,8 @@ public interface SyncopeStreamPushExecutor {
             AnyType anyType,
             List<? extends Any<?>> anys,
             List<String> columns,
-            StreamConnector connector,
+            Connector connector,
+            List<String> propagationActions,
             PushTaskTO pushTaskTO,
             String executor)
             throws JobExecutionException;
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java
index 2a2b178..1e14e94 100644
--- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java
@@ -29,7 +29,6 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import java.text.ParseException;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -62,9 +61,9 @@ public class IntAttrNameParserTest {
     private static final Map<AnyTypeKind, List<String>> FIELDS = new HashMap<>();
 
     static {
-        FIELDS.put(AnyTypeKind.USER, Arrays.asList("key", "username"));
-        FIELDS.put(AnyTypeKind.GROUP, Arrays.asList("key", "name", "userOwner"));
-        FIELDS.put(AnyTypeKind.ANY_OBJECT, Arrays.asList("key", "name"));
+        FIELDS.put(AnyTypeKind.USER, List.of("key", "username"));
+        FIELDS.put(AnyTypeKind.GROUP, List.of("key", "name", "userOwner"));
+        FIELDS.put(AnyTypeKind.ANY_OBJECT, List.of("key", "name"));
     }
 
     @Mock
diff --git a/core/provisioning-java/pom.xml b/core/provisioning-java/pom.xml
index 12fe7f5..a2c7067 100644
--- a/core/provisioning-java/pom.xml
+++ b/core/provisioning-java/pom.xml
@@ -59,6 +59,11 @@ under the License.
     </dependency>
     
     <dependency>
+      <groupId>com.fasterxml.jackson.dataformat</groupId>
+      <artifactId>jackson-dataformat-csv</artifactId>
+    </dependency>
+
+    <dependency>
       <groupId>org.codehaus.groovy</groupId>
       <artifactId>groovy</artifactId>
     </dependency>
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java
index 41637c6..e7a96d9 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java
@@ -19,7 +19,6 @@
 package org.apache.syncope.core.provisioning.java.pushpull;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
@@ -160,7 +159,7 @@ public class OutboundMatcher {
                         rule.get().getFilter(any, provision),
                         provision,
                         ArrayUtils.isEmpty(linkingItems)
-                        ? Optional.empty() : Optional.of(Arrays.asList(linkingItems)),
+                        ? Optional.empty() : Optional.of(List.of(linkingItems)),
                         Optional.empty()));
             } else {
                 Optional<? extends MappingItem> connObjectKeyItem = MappingUtils.getConnObjectKeyItem(provision);
@@ -173,7 +172,7 @@ public class OutboundMatcher {
                             connObjectKeyValue.get(),
                             provision,
                             ArrayUtils.isEmpty(linkingItems)
-                            ? Optional.empty() : Optional.of(Arrays.asList(linkingItems)),
+                            ? Optional.empty() : Optional.of(List.of(linkingItems)),
                             Optional.empty()).
                             ifPresent(result::add);
                 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/StreamConnector.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/CSVStreamConnector.java
similarity index 60%
rename from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/StreamConnector.java
rename to core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/CSVStreamConnector.java
index 5ff51c8..6b025ae 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/StreamConnector.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/CSVStreamConnector.java
@@ -16,19 +16,27 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.provisioning.api.pushpull.stream;
+package org.apache.syncope.core.provisioning.java.pushpull.stream;
 
 import com.fasterxml.jackson.databind.MappingIterator;
 import com.fasterxml.jackson.databind.SequenceWriter;
+import com.fasterxml.jackson.dataformat.csv.CsvMapper;
+import com.fasterxml.jackson.dataformat.csv.CsvSchema;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.rest.api.beans.CSVPullSpec;
 import org.apache.syncope.core.persistence.api.entity.ConnInstance;
 import org.apache.syncope.core.provisioning.api.Connector;
+import org.identityconnectors.framework.common.exceptions.ConnectorException;
 import org.identityconnectors.framework.common.objects.Attribute;
 import org.identityconnectors.framework.common.objects.AttributeBuilder;
 import org.identityconnectors.framework.common.objects.AttributeUtil;
@@ -43,28 +51,78 @@ import org.identityconnectors.framework.common.objects.SyncToken;
 import org.identityconnectors.framework.common.objects.Uid;
 import org.identityconnectors.framework.common.objects.filter.Filter;
 import org.identityconnectors.framework.spi.SearchResultsHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.util.CollectionUtils;
 
-public class StreamConnector implements Connector {
+public class CSVStreamConnector implements Connector, AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CSVStreamConnector.class);
 
     private final String keyColumn;
 
     private final String arrayElementsSeparator;
 
-    private final MappingIterator<Map<String, String>> reader;
+    private final CsvSchema.Builder schemaBuilder;
+
+    private final InputStream in;
+
+    private final OutputStream out;
+
+    private MappingIterator<Map<String, String>> reader;
 
-    private final SequenceWriter writer;
+    private SequenceWriter writer;
 
-    public StreamConnector(
+    public CSVStreamConnector(
             final String keyColumn,
             final String arrayElementsSeparator,
-            final MappingIterator<Map<String, String>> reader,
-            final SequenceWriter writer) {
+            final CsvSchema.Builder schemaBuilder,
+            final InputStream in,
+            final OutputStream out) {
 
         this.keyColumn = keyColumn;
         this.arrayElementsSeparator = arrayElementsSeparator;
-        this.reader = reader;
-        this.writer = writer;
+        this.schemaBuilder = schemaBuilder;
+        this.in = in;
+        this.out = out;
+    }
+
+    @Override
+    public void close() throws IOException {
+        if (reader != null) {
+            reader.close();
+        }
+        if (writer != null) {
+            writer.close();
+        }
+    }
+
+    public MappingIterator<Map<String, String>> reader() throws IOException {
+        synchronized (this) {
+            if (reader == null) {
+                reader = new CsvMapper().readerFor(Map.class).with(schemaBuilder.build()).readValues(in);
+            }
+        }
+        return reader;
+    }
+
+    public List<String> getColumns(final CSVPullSpec spec) throws IOException {
+        List<String> columns = new ArrayList<>();
+        ((CsvSchema) reader().getParserSchema()).forEach(column -> {
+            if (!spec.getIgnoreColumns().contains(column.getName())) {
+                columns.add(column.getName());
+            }
+        });
+        return columns;
+    }
+
+    public SequenceWriter writer() throws IOException {
+        synchronized (this) {
+            if (writer == null) {
+                writer = new CsvMapper().writerFor(Map.class).with(schemaBuilder.build()).writeValues(out);
+            }
+        }
+        return writer;
     }
 
     @Override
@@ -84,29 +142,34 @@ public class StreamConnector implements Connector {
             final OperationOptions options,
             final AtomicReference<Boolean> propagationAttempted) {
 
-        if (writer != null) {
-            Map<String, String> row = new HashMap<>();
-            attrs.stream().filter(attr -> !AttributeUtil.isSpecial(attr)).forEach(attr -> {
-                if (CollectionUtils.isEmpty(attr.getValue()) || attr.getValue().get(0) == null) {
-                    row.put(attr.getName(), null);
-                } else if (attr.getValue().size() == 1) {
-                    row.put(attr.getName(), attr.getValue().get(0).toString());
-                } else if (arrayElementsSeparator == null) {
-                    row.put(attr.getName(), attr.getValue().toString());
-                } else {
-                    row.put(
-                            attr.getName(),
-                            attr.getValue().stream().map(Object::toString).
-                                    collect(Collectors.joining(arrayElementsSeparator)));
-                }
-            });
-            try {
-                writer.write(row);
-            } catch (IOException e) {
-                throw new IllegalStateException("Could not object " + row, e);
+        synchronized (schemaBuilder) {
+            if (schemaBuilder.size() == 0) {
+                attrs.stream().filter(attr -> !AttributeUtil.isSpecial(attr)).map(Attribute::getName).
+                        forEachOrdered(schemaBuilder::addColumn);
+            }
+        }
+
+        Map<String, String> row = new HashMap<>();
+        attrs.stream().filter(attr -> !AttributeUtil.isSpecial(attr)).forEach(attr -> {
+            if (CollectionUtils.isEmpty(attr.getValue()) || attr.getValue().get(0) == null) {
+                row.put(attr.getName(), null);
+            } else if (attr.getValue().size() == 1) {
+                row.put(attr.getName(), attr.getValue().get(0).toString());
+            } else if (arrayElementsSeparator == null) {
+                row.put(attr.getName(), attr.getValue().toString());
+            } else {
+                row.put(
+                        attr.getName(),
+                        attr.getValue().stream().map(Object::toString).
+                                collect(Collectors.joining(arrayElementsSeparator)));
             }
-            propagationAttempted.set(Boolean.TRUE);
+        });
+        try {
+            writer().write(row);
+        } catch (IOException e) {
+            throw new ConnectorException("Could not write object " + row, e);
         }
+        propagationAttempted.set(Boolean.TRUE);
         return null;
     }
 
@@ -165,9 +228,9 @@ public class StreamConnector implements Connector {
 
         SearchResult result = new SearchResult();
 
-        if (reader != null) {
-            while (reader.hasNext()) {
-                Map<String, String> row = reader.next();
+        try {
+            while (reader().hasNext()) {
+                Map<String, String> row = reader().next();
 
                 ConnectorObjectBuilder builder = new ConnectorObjectBuilder();
                 builder.setObjectClass(objectClass);
@@ -181,6 +244,9 @@ public class StreamConnector implements Connector {
 
                 handler.handle(builder.build());
             }
+        } catch (IOException e) {
+            LOG.error("Could not read CSV from provided stream", e);
+            throw new ConnectorException(e);
         }
 
         return result;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java
index ff6553e..2ce44a0 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java
@@ -47,8 +47,8 @@ import org.apache.syncope.core.provisioning.api.pushpull.GroupPullResultHandler;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
 import org.apache.syncope.common.lib.to.ProvisioningReport;
 import org.apache.syncope.common.lib.types.IdMImplementationType;
+import org.apache.syncope.core.provisioning.api.Connector;
 import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
-import org.apache.syncope.core.provisioning.api.pushpull.stream.StreamConnector;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler;
 import org.apache.syncope.core.spring.ImplementationManager;
 import org.quartz.JobExecutionException;
@@ -168,7 +168,7 @@ public class StreamPullJobDelegate extends PullJobDelegate implements SyncopeStr
             final List<String> columns,
             final ConflictResolutionAction conflictResolutionAction,
             final String pullCorrelationRule,
-            final StreamConnector connector,
+            final Connector connector,
             final PullTaskTO pullTaskTO) throws JobExecutionException {
 
         LOG.debug("Executing stream pull");
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegate.java
index 1b775d4..cf18b9f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegate.java
@@ -34,11 +34,11 @@ import org.apache.syncope.core.persistence.api.entity.resource.Mapping;
 import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.persistence.api.entity.task.PushTask;
+import org.apache.syncope.core.provisioning.api.Connector;
 import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPushResultHandler;
 import org.apache.syncope.core.provisioning.api.pushpull.GroupPushResultHandler;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
 import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
-import org.apache.syncope.core.provisioning.api.pushpull.stream.StreamConnector;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopePushResultHandler;
 import org.apache.syncope.core.provisioning.api.pushpull.stream.SyncopeStreamPushExecutor;
 import org.apache.syncope.core.provisioning.api.pushpull.UserPushResultHandler;
@@ -88,7 +88,9 @@ public class StreamPushJobDelegate extends PushJobDelegate implements SyncopeStr
     }
 
     private ExternalResource externalResource(
-            final AnyType anyType, final List<String> columns) throws JobExecutionException {
+            final AnyType anyType,
+            final List<String> columns,
+            final List<String> propagationActions) throws JobExecutionException {
 
         Provision provision = entityFactory.newEntity(Provision.class);
         provision.setAnyType(anyType);
@@ -118,6 +120,15 @@ public class StreamPushJobDelegate extends PushJobDelegate implements SyncopeStr
         resource.add(provision);
         provision.setResource(resource);
 
+        propagationActions.forEach(key -> {
+            Implementation impl = implementationDAO.find(key);
+            if (impl == null || !IdMImplementationType.PROPAGATION_ACTIONS.equals(impl.getType())) {
+                LOG.debug("Invalid " + Implementation.class.getSimpleName() + " {}, ignoring...", key);
+            } else {
+                resource.add(impl);
+            }
+        });
+
         return resource;
     }
 
@@ -126,21 +137,22 @@ public class StreamPushJobDelegate extends PushJobDelegate implements SyncopeStr
             final AnyType anyType,
             final List<? extends Any<?>> anys,
             final List<String> columns,
-            final StreamConnector connector,
+            final Connector connector,
+            final List<String> propagationActions,
             final PushTaskTO pushTaskTO,
             final String executor) throws JobExecutionException {
 
         LOG.debug("Executing stream push as {}", executor);
         this.executor = executor;
 
-        List<PushActions> actions = new ArrayList<>();
+        List<PushActions> pushActions = new ArrayList<>();
         pushTaskTO.getActions().forEach(key -> {
             Implementation impl = implementationDAO.find(key);
             if (impl == null || !IdMImplementationType.PUSH_ACTIONS.equals(impl.getType())) {
                 LOG.debug("Invalid " + Implementation.class.getSimpleName() + " {}, ignoring...", key);
             } else {
                 try {
-                    actions.add(ImplementationManager.build(impl));
+                    pushActions.add(ImplementationManager.build(impl));
                 } catch (Exception e) {
                     LOG.warn("While building {}", impl, e);
                 }
@@ -148,7 +160,7 @@ public class StreamPushJobDelegate extends PushJobDelegate implements SyncopeStr
         });
 
         try {
-            ExternalResource resource = externalResource(anyType, columns);
+            ExternalResource resource = externalResource(anyType, columns, propagationActions);
             Provision provision = resource.getProvisions().get(0);
 
             PushTask pushTask = entityFactory.newEntity(PushTask.class);
@@ -161,10 +173,10 @@ public class StreamPushJobDelegate extends PushJobDelegate implements SyncopeStr
             pushTask.setSyncStatus(false);
 
             profile = new ProvisioningProfile<>(connector, pushTask);
-            profile.getActions().addAll(actions);
+            profile.getActions().addAll(pushActions);
             profile.setConflictResolutionAction(ConflictResolutionAction.FIRSTMATCH);
 
-            for (PushActions action : actions) {
+            for (PushActions action : pushActions) {
                 action.beforeAll(profile);
             }
 
@@ -186,7 +198,7 @@ public class StreamPushJobDelegate extends PushJobDelegate implements SyncopeStr
 
             doHandle(anys, handler, provision.getResource());
 
-            for (PushActions action : actions) {
+            for (PushActions action : pushActions) {
                 action.afterAll(profile);
             }
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java
index 1f52585..a08c998 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java
@@ -19,7 +19,6 @@
 package org.apache.syncope.core.provisioning.java.utils;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
@@ -117,7 +116,7 @@ public final class MappingUtils {
         attrsToGet.add(Uid.NAME);
         attrsToGet.add(OperationalAttributes.ENABLE_NAME);
         if (!ArrayUtils.isEmpty(moreAttrsToGet)) {
-            attrsToGet.addAll(Arrays.asList(moreAttrsToGet));
+            attrsToGet.addAll(List.of(moreAttrsToGet));
         }
 
         items.filter(item -> item.getPurpose() != MappingPurpose.NONE).
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActionsTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActionsTest.java
index de4a6bb..1b833eb 100644
--- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActionsTest.java
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActionsTest.java
@@ -25,9 +25,12 @@ import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
 
-import org.apache.syncope.common.lib.patch.AnyPatch;
-import org.apache.syncope.common.lib.patch.PasswordPatch;
-import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.request.AnyCR;
+import org.apache.syncope.common.lib.request.AnyUR;
+import org.apache.syncope.common.lib.request.PasswordPatch;
+import org.apache.syncope.common.lib.request.UserCR;
+import org.apache.syncope.common.lib.request.UserUR;
 import org.apache.syncope.common.lib.to.EntityTO;
 import org.apache.syncope.common.lib.to.ProvisioningReport;
 import org.apache.syncope.common.lib.to.UserTO;
@@ -53,9 +56,6 @@ public class LDAPPasswordPullActionsTest extends AbstractTest {
     private ProvisioningProfile<?, ?> profile;
 
     @Mock
-    private AnyPatch anyPatch;
-
-    @Mock
     private UserDAO userDAO;
 
     @Mock
@@ -64,6 +64,10 @@ public class LDAPPasswordPullActionsTest extends AbstractTest {
     @InjectMocks
     private LDAPPasswordPullActions ldapPasswordPullActions;
 
+    private AnyCR anyCR;
+
+    private AnyUR anyUR;
+
     private EntityTO entity;
 
     private String encodedPassword;
@@ -84,9 +88,10 @@ public class LDAPPasswordPullActionsTest extends AbstractTest {
     public void beforeProvision() throws JobExecutionException {
         String digest = "SHA256";
         String password = "t3stPassw0rd";
-        ReflectionTestUtils.setField(entity, "password", String.format("{%s}%s", digest, password));
+        anyCR = new UserCR.Builder(SyncopeConstants.ROOT_REALM, "username").
+                password(String.format("{%s}%s", digest, password)).build();
 
-        ldapPasswordPullActions.beforeProvision(profile, syncDelta, entity);
+        ldapPasswordPullActions.beforeProvision(profile, syncDelta, anyCR);
 
         assertEquals(CipherAlgorithm.valueOf(digest), ReflectionTestUtils.getField(ldapPasswordPullActions, "cipher"));
         assertEquals(password, ReflectionTestUtils.getField(ldapPasswordPullActions, "encodedPassword"));
@@ -94,14 +99,11 @@ public class LDAPPasswordPullActionsTest extends AbstractTest {
 
     @Test
     public void beforeUpdate() throws JobExecutionException {
-        anyPatch = new UserPatch();
-        PasswordPatch passwordPatch = new PasswordPatch();
-        String digest = "MD5";
-        String password = "an0therTestP4ss";
-        ReflectionTestUtils.setField(passwordPatch, "value", String.format("{%s}%s", digest, password));
-        ReflectionTestUtils.setField(anyPatch, "password", passwordPatch);
+        anyUR = new UserUR.Builder(null).
+                password(new PasswordPatch.Builder().value("{MD5}an0therTestP4ss").build()).
+                build();
 
-        ldapPasswordPullActions.beforeUpdate(profile, syncDelta, entity, anyPatch);
+        ldapPasswordPullActions.beforeUpdate(profile, syncDelta, entity, anyUR);
 
         assertNull(ReflectionTestUtils.getField(ldapPasswordPullActions, "encodedPassword"));
     }
@@ -126,5 +128,4 @@ public class LDAPPasswordPullActionsTest extends AbstractTest {
         assertNull(ReflectionTestUtils.getField(ldapPasswordPullActions, "encodedPassword"));
         assertNull(ReflectionTestUtils.getField(ldapPasswordPullActions, "cipher"));
     }
-
 }
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegateTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegateTest.java
index dfa523c..bb57c94 100644
--- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegateTest.java
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegateTest.java
@@ -20,15 +20,11 @@ package org.apache.syncope.core.provisioning.java.pushpull.stream;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
-import com.fasterxml.jackson.databind.MappingIterator;
+import com.fasterxml.jackson.dataformat.csv.CsvSchema;
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
-import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.stream.Collectors;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.to.PullTaskTO;
@@ -41,7 +37,7 @@ import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.common.lib.to.ProvisioningReport;
-import org.apache.syncope.core.provisioning.api.pushpull.stream.StreamConnector;
+import org.apache.syncope.common.rest.api.beans.CSVPullSpec;
 import org.apache.syncope.core.provisioning.api.pushpull.stream.SyncopeStreamPullExecutor;
 import org.apache.syncope.core.provisioning.java.AbstractTest;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
@@ -64,39 +60,51 @@ public class StreamPullJobDelegateTest extends AbstractTest {
 
     @Test
     public void pull() throws JobExecutionException, IOException {
+        List<String> columns = List.of(
+                "username",
+                "email",
+                "surname",
+                "firstname",
+                "fullname",
+                "userId");
+
+        StringBuilder csv = new StringBuilder();
+        csv.append(columns.stream().collect(Collectors.joining(",")));
+        csv.append('\n');
+        csv.append("donizetti,");
+        csv.append("donizetti@apache.org,");
+        csv.append("Donizetti,");
+        csv.append("Gaetano,");
+        csv.append("Gaetano Donizetti,");
+        csv.append("donizetti@apache.org");
+        csv.append('\n');
+
         PullTaskTO pullTask = new PullTaskTO();
         pullTask.setDestinationRealm(SyncopeConstants.ROOT_REALM);
         pullTask.setRemediation(false);
         pullTask.setMatchingRule(MatchingRule.UPDATE);
         pullTask.setUnmatchingRule(UnmatchingRule.PROVISION);
 
-        Map<String, String> user = new HashMap<>();
-        user.put("username", "donizetti");
-        user.put("email", "donizetti@apache.org");
-        user.put("surname", "Donizetti");
-        user.put("firstname", "Gaetano");
-        user.put("fullname", "Gaetano Donizetti");
-        user.put("userId", "donizetti@apache.org");
-        Iterator<Map<String, String>> backing = List.of(user).iterator();
-
-        @SuppressWarnings("unchecked")
-        MappingIterator<Map<String, String>> itor = mock(MappingIterator.class);
-        when(itor.hasNext()).thenAnswer(invocation -> backing.hasNext());
-        when(itor.next()).thenAnswer(invocation -> backing.next());
+        List<ProvisioningReport> results = AuthContextUtils.callAsAdmin(SyncopeConstants.MASTER_DOMAIN, () -> {
+            try (CSVStreamConnector connector = new CSVStreamConnector(
+                    "username",
+                    ";",
+                    new CsvSchema.Builder().setUseHeader(true),
+                    new ByteArrayInputStream(csv.toString().getBytes()),
+                    null)) {
 
-        List<String> columns = user.keySet().stream().collect(Collectors.toList());
+                List<String> csvColumns = connector.getColumns(new CSVPullSpec());
+                assertEquals(columns, csvColumns);
 
-        List<ProvisioningReport> results = AuthContextUtils.callAsAdmin(SyncopeConstants.MASTER_DOMAIN, () -> {
-            try {
                 return streamPullExecutor.pull(
                         anyTypeDAO.findUser(),
                         "username",
                         columns,
                         ConflictResolutionAction.IGNORE,
                         null,
-                        new StreamConnector("username", null, itor, null),
+                        connector,
                         pullTask);
-            } catch (JobExecutionException e) {
+            } catch (Exception e) {
                 throw new RuntimeException(e);
             }
         });
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegateTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegateTest.java
index 2fcab3c..7742760 100644
--- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegateTest.java
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegateTest.java
@@ -20,18 +20,17 @@ package org.apache.syncope.core.provisioning.java.pushpull.stream;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import com.fasterxml.jackson.databind.MappingIterator;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SequenceWriter;
+import com.fasterxml.jackson.dataformat.csv.CsvMapper;
+import com.fasterxml.jackson.dataformat.csv.CsvSchema;
 import java.io.IOException;
 import java.io.PipedInputStream;
 import java.io.PipedOutputStream;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.to.ProvisioningReport;
 import org.apache.syncope.common.lib.to.PushTaskTO;
@@ -39,7 +38,6 @@ import org.apache.syncope.common.lib.types.MatchingRule;
 import org.apache.syncope.common.lib.types.UnmatchingRule;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.provisioning.api.pushpull.stream.StreamConnector;
 import org.apache.syncope.core.provisioning.api.pushpull.stream.SyncopeStreamPushExecutor;
 import org.apache.syncope.core.provisioning.java.AbstractTest;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
@@ -50,8 +48,6 @@ import org.springframework.transaction.annotation.Transactional;
 @Transactional("Master")
 public class StreamPushJobDelegateTest extends AbstractTest {
 
-    private static final ObjectMapper MAPPER = new ObjectMapper();
-
     @Autowired
     private SyncopeStreamPushExecutor streamPushExecutor;
 
@@ -71,14 +67,19 @@ public class StreamPushJobDelegateTest extends AbstractTest {
         pushTask.setUnmatchingRule(UnmatchingRule.PROVISION);
 
         List<ProvisioningReport> results = AuthContextUtils.callAsAdmin(SyncopeConstants.MASTER_DOMAIN, () -> {
-            try (SequenceWriter writer = MAPPER.writer().forType(Map.class).writeValues(os)) {
-                writer.init(true);
+            try (CSVStreamConnector connector = new CSVStreamConnector(
+                    null,
+                    ";",
+                    new CsvSchema.Builder().setUseHeader(true),
+                    null,
+                    os)) {
 
                 return streamPushExecutor.push(
                         anyTypeDAO.findUser(),
                         userDAO.findAll(1, 100),
-                        Arrays.asList("username", "firstname", "surname", "email", "status", "loginDate"),
-                        new StreamConnector(null, null, null, writer),
+                        List.of("username", "firstname", "surname", "email", "status", "loginDate"),
+                        connector,
+                        List.of(),
                         pushTask,
                         "user");
             } catch (Exception e) {
@@ -87,7 +88,8 @@ public class StreamPushJobDelegateTest extends AbstractTest {
         });
         assertEquals(userDAO.count(), results.size());
 
-        MappingIterator<Map<String, String>> reader = MAPPER.readerFor(Map.class).readValues(in);
+        MappingIterator<Map<String, String>> reader =
+                new CsvMapper().readerFor(Map.class).with(CsvSchema.emptySchema().withHeader()).readValues(in);
 
         for (int i = 0; i < results.size() && reader.hasNext(); i++) {
             Map<String, String> row = reader.next();
@@ -97,18 +99,18 @@ public class StreamPushJobDelegateTest extends AbstractTest {
 
             switch (row.get("username")) {
                 case "rossini":
-                    assertNull(row.get("email"));
-                    assertTrue(row.get("loginDate").contains(","));
+                    assertEquals(StringUtils.EMPTY, row.get("email"));
+                    assertTrue(row.get("loginDate").contains(";"));
                     break;
 
                 case "verdi":
                     assertEquals("verdi@syncope.org", row.get("email"));
-                    assertNull(row.get("loginDate"));
+                    assertEquals(StringUtils.EMPTY, row.get("loginDate"));
                     break;
 
                 case "bellini":
-                    assertNull(row.get("email"));
-                    assertFalse(row.get("loginDate").contains(","));
+                    assertEquals(StringUtils.EMPTY, row.get("email"));
+                    assertFalse(row.get("loginDate").contains(";"));
                     break;
 
                 default:
diff --git a/core/spring/pom.xml b/core/spring/pom.xml
index 71a03c9..b301382 100644
--- a/core/spring/pom.xml
+++ b/core/spring/pom.xml
@@ -101,8 +101,13 @@ under the License.
       <version>${project.version}</version>
     </dependency>        
     <dependency>
-      <groupId>org.apache.syncope.common.idrepo</groupId>
-      <artifactId>syncope-common-idrepo-rest-api</artifactId>
+      <groupId>org.apache.syncope.common.idm</groupId>
+      <artifactId>syncope-common-idm-rest-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.common.am</groupId>
+      <artifactId>syncope-common-am-rest-api</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/BpmnProcessRestClient.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/BpmnProcessRestClient.java
index 12eb10c..4924163 100644
--- a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/BpmnProcessRestClient.java
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/rest/BpmnProcessRestClient.java
@@ -19,7 +19,6 @@
 package org.apache.syncope.client.console.rest;
 
 import java.io.InputStream;
-import java.util.Collections;
 import java.util.List;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
@@ -44,8 +43,8 @@ public class BpmnProcessRestClient extends BaseRestClient {
         BpmnProcessService service = getService(BpmnProcessService.class);
 
         MetadataMap<String, String> headers = new MetadataMap<>();
-        headers.put(HttpHeaders.CONTENT_TYPE, Collections.singletonList(mediaType.toString()));
-        headers.put(HttpHeaders.ACCEPT, Collections.singletonList(mediaType.toString()));
+        headers.put(HttpHeaders.CONTENT_TYPE, List.of(mediaType.toString()));
+        headers.put(HttpHeaders.ACCEPT, List.of(mediaType.toString()));
         WebClient.client(service).headers(headers);
 
         return service;
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2UserManager.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2UserManager.java
index 1e7c335..c3c0a3e 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2UserManager.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2UserManager.java
@@ -20,7 +20,6 @@ package org.apache.syncope.core.logic.saml2;
 
 import java.text.ParseException;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
 import java.util.stream.Collectors;
@@ -92,7 +91,7 @@ public class SAML2UserManager {
         SAML2IdP idp = idpDAO.find(idpKey);
         if (idp == null) {
             LOG.warn("Invalid IdP: {}", idpKey);
-            return Collections.emptyList();
+            return List.of();
         }
 
         return inboundMatcher.matchByConnObjectKeyValue(
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/LogsITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/LogsITCase.java
index acdf767..f039075 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/LogsITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/LogsITCase.java
@@ -73,8 +73,7 @@ public class LogsITCase extends AbstractConsoleITCase {
 
     @Test
     public void readConsoleLogs() {
-        TESTER.assertComponent("body:content:tabbedPanel:tabs-container:tabs:1:link",
-                AjaxFallbackLink.class);
+        TESTER.assertComponent("body:content:tabbedPanel:tabs-container:tabs:1:link", AjaxFallbackLink.class);
         TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:1:link");
         TESTER.assertComponent(CONTAINER_PATH, WebMarkupContainer.class);
 
@@ -91,6 +90,8 @@ public class LogsITCase extends AbstractConsoleITCase {
 
         TESTER.getRequest().addParameter(
                 result.getPageRelativePath() + ":fields:1:field:dropDownChoiceField", "6");
+        TESTER.assertComponent(
+                result.getPageRelativePath() + ":fields:1:field:dropDownChoiceField", DropDownChoice.class);
         TESTER.executeAjaxEvent(
                 result.getPageRelativePath() + ":fields:1:field:dropDownChoiceField", Constants.ON_CHANGE);
 
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ReconciliationITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ReconciliationITCase.java
index 822bb5f..5b2a20c 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ReconciliationITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ReconciliationITCase.java
@@ -251,17 +251,8 @@ public class ReconciliationITCase extends AbstractITCase {
         PagedResult<UserTO> users = userService.search(anyQuery);
         assertNotNull(users);
 
-        CsvSchema.Builder builder = CsvSchema.builder().setUseHeader(true);
-        builder.addColumn("username");
-        builder.addColumn("status");
-        builder.addColumn("firstname");
-        builder.addColumn("surname");
-        builder.addColumn("email");
-        builder.addColumn("loginDate");
-        CsvSchema schema = builder.build();
-
-        MappingIterator<Map<String, String>> reader = new CsvMapper().readerFor(Map.class).with(schema).
-                readValues((InputStream) response.getEntity());
+        MappingIterator<Map<String, String>> reader = new CsvMapper().readerFor(Map.class).
+                with(CsvSchema.emptySchema().withHeader()).readValues((InputStream) response.getEntity());
 
         int rows = 0;
         for (; reader.hasNext(); rows++) {