You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by an...@apache.org on 2021/05/28 15:25:33 UTC

[syncope] branch master updated: creating remediation also from exceptions on pullActions, also fixing… (#269) (#271)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 75bf23a  creating remediation also from exceptions on pullActions, also fixing… (#269) (#271)
75bf23a is described below

commit 75bf23a78c918f6ec27691252c96176bcf215fe1
Author: Andrea Patricelli <an...@apache.org>
AuthorDate: Fri May 28 17:25:00 2021 +0200

    creating remediation also from exceptions on pullActions, also fixing… (#269) (#271)
    
    * enabling remediation creation also from exceptions on pullActions
---
 .../client/console/status/ReconTaskPanel.java      |  4 ++
 .../client/console/status/ReconTaskPanel.html      |  1 +
 .../console/status/ReconTaskPanel.properties       | 24 +++++++++
 .../console/status/ReconTaskPanel_fr_CA.properties | 24 +++++++++
 .../console/status/ReconTaskPanel_it.properties    | 24 +++++++++
 .../console/status/ReconTaskPanel_ja.properties    | 24 +++++++++
 .../console/status/ReconTaskPanel_pt_BR.properties | 24 +++++++++
 .../console/status/ReconTaskPanel_ru.properties}   | 15 +-----
 .../SchedTaskWizardBuilder$Profile_ru.properties   |  2 +-
 .../syncope/core/logic/ReconciliationLogic.java    |  4 ++
 .../java/pushpull/AbstractPullResultHandler.java   | 27 ++++++----
 .../apache/syncope/fit/core/PullTaskITCase.java    | 58 ++++++++++++++++++++++
 12 files changed, 206 insertions(+), 25 deletions(-)

diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java
index f6bce2f..80349b1 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java
@@ -114,6 +114,7 @@ public class ReconTaskPanel extends MultilevelPanel.SecondLevel {
 
         if (taskTO instanceof PushTaskTO) {
             form.add(new Label("realm", ""));
+            form.add(new Label("remediation", ""));
         } else {
             boolean isSearchEnabled = RealmsUtils.isSearchEnabled();
             AutoCompleteSettings settings = new AutoCompleteSettings();
@@ -147,6 +148,9 @@ public class ReconTaskPanel extends MultilevelPanel.SecondLevel {
                 realm.setEnabled(false);
             }
             form.add(realm);
+
+            form.add(new AjaxCheckBoxPanel(
+                    "remediation", "remediation", new PropertyModel<>(taskTO, "remediation"), false));
         }
 
         AjaxPalettePanel<String> actions = new AjaxPalettePanel.Builder<String>().
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel.html b/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel.html
index 8c7a2b0..6e3aabd 100644
--- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel.html
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel.html
@@ -22,6 +22,7 @@ under the License.
       <div class="form-group form-palette">
         <div class="row" wicket:id="actions">[actions]</div>
       </div>
+      <div class="form-group"><span wicket:id="remediation">[remediation]</span></div>
       <div class="form-group"><span wicket:id="realm">[realm]</span></div>
       <div class="form-group"><span wicket:id="matchingRule">[matchingRule]</span></div>
       <div class="form-group"><span wicket:id="unmatchingRule">[unmatchingRule]</span></div>
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel.properties
new file mode 100644
index 0000000..643ed9a
--- /dev/null
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel.properties
@@ -0,0 +1,24 @@
+# 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.
+destinationRealm=Destination realm
+remediation=Remediation
+matchingRule=Matching rule
+unmatchingRule=Unmatching rule
+performCreate=Allow create
+performUpdate=Allow update
+performDelete=Allow delete
+syncStatus=Sync status
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel_fr_CA.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel_fr_CA.properties
new file mode 100644
index 0000000..0736c11
--- /dev/null
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel_fr_CA.properties
@@ -0,0 +1,24 @@
+# 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.
+destinationRealm=Domaine de destination
+remediation=Remise en \u00e9tat
+matchingRule=R\u00e8gle correspondante
+unmatchingRule=R\u00e8gle non correspondante
+performCreate=Permettre cr\u00e9ation
+performUpdate=Permettre mise \u00e0 jour
+performDelete=Permettre suppression
+syncStatus=Statut sync
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel_it.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel_it.properties
new file mode 100644
index 0000000..892ee6f
--- /dev/null
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel_it.properties
@@ -0,0 +1,24 @@
+# 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.
+destinationRealm=Realm destinazione
+remediation=Bonifica
+matchingRule=Regola di matching
+unmatchingRule=Regola di unmatching
+performCreate=Consenti creazione
+performUpdate=Consenti modifica
+performDelete=Consenti cancellazione
+syncStatus=Allinea stato
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel_ja.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel_ja.properties
new file mode 100644
index 0000000..438987f
--- /dev/null
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel_ja.properties
@@ -0,0 +1,24 @@
+# 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.
+destinationRealm=\u5b9b\u5148\u30ec\u30eb\u30e0
+remediation=\u5fa9\u65e7
+matchingRule=\u4e00\u81f4\u30eb\u30fc\u30eb
+unmatchingRule=\u4e0d\u4e00\u81f4\u30eb\u30fc\u30eb
+performCreate=\u4f5c\u6210\u3092\u8a31\u53ef
+performUpdate=\u66f4\u65b0\u3092\u8a31\u53ef
+performDelete=\u524a\u9664\u3092\u8a31\u53ef
+syncStatus=\u540c\u671f\u30b9\u30c6\u30fc\u30bf\u30b9
diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel_pt_BR.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel_pt_BR.properties
new file mode 100644
index 0000000..643ed9a
--- /dev/null
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel_pt_BR.properties
@@ -0,0 +1,24 @@
+# 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.
+destinationRealm=Destination realm
+remediation=Remediation
+matchingRule=Matching rule
+unmatchingRule=Unmatching rule
+performCreate=Allow create
+performUpdate=Allow update
+performDelete=Allow delete
+syncStatus=Sync status
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder$Profile_ru.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel_ru.properties
similarity index 64%
copy from client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder$Profile_ru.properties
copy to client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel_ru.properties
index 18bbca6..d2f44ec 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder$Profile_ru.properties
+++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/status/ReconTaskPanel_ru.properties
@@ -14,22 +14,11 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-#
-name=\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435
-description=\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435
-jobDelegate=\u041a\u043b\u0430\u0441\u0441
+destinationRealm=\u041e\u0431\u043b\u0430\u0441\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f
+remediation=\u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435
 matchingRule=\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043f\u0440\u0438 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0438
 unmatchingRule=\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043f\u0440\u0438 \u043d\u0435\u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0438
 performCreate=\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435
 performUpdate=\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435
 performDelete=\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435
 syncStatus=\u0421\u0442\u0430\u0442\u0443\u0441 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438
-lastExec=\u041f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0439 \u0437\u0430\u043f\u0443\u0441\u043a
-nextExec=\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0437\u0430\u043f\u0443\u0441\u043a
-detail=\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435
-delete=\u0423\u0434\u0430\u043b\u0438\u0442\u044c
-edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c
-execute=\u0417\u0430\u043f\u0443\u0441\u043a
-executeDryRun=\u041f\u0440\u043e\u0431\u043d\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a
-latestExecStatus=\u0421\u0442\u0430\u0442\u0443\u0441 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430
-remediation=Remediation
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder$Profile_ru.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder$Profile_ru.properties
index 18bbca6..16107dd 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder$Profile_ru.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder$Profile_ru.properties
@@ -32,4 +32,4 @@ edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c
 execute=\u0417\u0430\u043f\u0443\u0441\u043a
 executeDryRun=\u041f\u0440\u043e\u0431\u043d\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a
 latestExecStatus=\u0421\u0442\u0430\u0442\u0443\u0441 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430
-remediation=Remediation
+remediation=\u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435
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 8d39b68..5070bd1 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
@@ -104,6 +104,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Component;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.transaction.annotation.Transactional;
 
 @Component
 public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
@@ -455,6 +456,7 @@ public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
     }
 
     @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_EXECUTE + "')")
+    @Transactional(noRollbackFor = SyncopeClientException.class)
     public List<ProvisioningReport> pull(
             final String anyTypeKey,
             final String resourceKey,
@@ -485,6 +487,7 @@ public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
     }
 
     @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_EXECUTE + "')")
+    @Transactional(noRollbackFor = SyncopeClientException.class)
     public List<ProvisioningReport> pull(
             final String anyTypeKey,
             final String resourceKey,
@@ -628,6 +631,7 @@ public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
     }
 
     @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_EXECUTE + "')")
+    @Transactional(noRollbackFor = SyncopeClientException.class)
     public List<ProvisioningReport> pull(final CSVPullSpec spec, final InputStream csv) {
         AnyType anyType = anyTypeDAO.find(spec.getAnyTypeKey());
         if (anyType == null) {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
index dbaa8a4..22b8bb9 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
@@ -58,6 +58,7 @@ import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionExceptio
 import org.apache.syncope.common.lib.to.ProvisioningReport;
 import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
 import org.apache.syncope.core.persistence.api.dao.PullMatch;
+import org.apache.syncope.core.persistence.api.dao.TaskDAO;
 import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheKey;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullExecutor;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler;
@@ -90,6 +91,9 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
     protected UserDAO userDAO;
 
     @Autowired
+    protected TaskDAO taskDAO;
+
+    @Autowired
     protected RemediationDAO remediationDAO;
 
     @Autowired
@@ -209,19 +213,18 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
             result.setKey(null);
             end(provision.getAnyType().getKind(), UnmatchingRule.toEventName(rule), Result.SUCCESS, null, null, delta);
         } else {
-            for (PullActions action : profile.getActions()) {
-                if (rule == UnmatchingRule.ASSIGN) {
-                    action.beforeAssign(profile, delta, anyCR);
-                } else if (rule == UnmatchingRule.PROVISION) {
-                    action.beforeProvision(profile, delta, anyCR);
-                }
-            }
-            result.setName(getName(anyCR));
-
             Object output;
             Result resultStatus;
-
             try {
+                for (PullActions action : profile.getActions()) {
+                    if (rule == UnmatchingRule.ASSIGN) {
+                        action.beforeAssign(profile, delta, anyCR);
+                    } else if (rule == UnmatchingRule.PROVISION) {
+                        action.beforeProvision(profile, delta, anyCR);
+                    }
+                }
+                result.setName(getName(anyCR));
+
                 AnyTO created = doCreate(anyCR, delta);
                 output = created;
                 result.setKey(created.getKey());
@@ -257,7 +260,9 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
                     entity.setError(result.getMessage());
                     entity.setInstant(new Date());
                     entity.setRemoteName(delta.getObject().getName().getNameValue());
-                    entity.setPullTask(profile.getTask());
+                    if (taskDAO.find(profile.getTask().getKey()) != null) {
+                        entity.setPullTask(profile.getTask());
+                    }
 
                     remediationDAO.save(entity);
                 }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
index f63272a..ee05318 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
@@ -86,12 +86,15 @@ import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.common.lib.types.ExecStatus;
 import org.apache.syncope.common.lib.types.IdMImplementationType;
 import org.apache.syncope.common.lib.types.IdRepoImplementationType;
+import org.apache.syncope.common.lib.types.MatchingRule;
 import org.apache.syncope.common.lib.types.ResourceDeassociationAction;
 import org.apache.syncope.common.lib.types.PullMode;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.common.lib.types.UnmatchingRule;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 import org.apache.syncope.common.rest.api.beans.AnyQuery;
+import org.apache.syncope.common.rest.api.beans.ReconQuery;
 import org.apache.syncope.common.rest.api.beans.RemediationQuery;
 import org.apache.syncope.common.rest.api.beans.TaskQuery;
 import org.apache.syncope.common.rest.api.service.ConnectorService;
@@ -814,6 +817,61 @@ public class PullTaskITCase extends AbstractTaskITCase {
     }
 
     @Test
+    public void remediationSinglePull() throws IOException {
+        // First of all, clear any potential conflict with existing user / group
+        ldapCleanup();
+
+        ResourceTO ldap = resourceService.read(RESOURCE_NAME_LDAP);
+        ldap.setKey("ldapForRemediationSinglePull");
+
+        ProvisionTO provision = ldap.getProvision(AnyTypeKind.USER.name()).get();
+        provision.getMapping().getItems().removeIf(item -> "userId".equals(item.getIntAttrName()));
+        provision.getMapping().getItems().removeIf(item -> "email".equals(item.getIntAttrName()));
+        provision.getVirSchemas().clear();
+
+        ldap.getProvisions().clear();
+        ldap.getProvisions().add(provision);
+
+        ldap = createResource(ldap);
+
+        try {
+            // 2. pull an user
+            PullTaskTO pullTask = new PullTaskTO();
+            pullTask.setResource(ldap.getKey());
+            pullTask.setDestinationRealm(SyncopeConstants.ROOT_REALM);
+            pullTask.setRemediation(true);
+            pullTask.setPerformCreate(true);
+            pullTask.setPerformUpdate(true);
+            pullTask.setUnmatchingRule(UnmatchingRule.ASSIGN);
+            pullTask.setMatchingRule(MatchingRule.UPDATE);
+
+            try {
+                reconciliationService.pull(new ReconQuery.Builder(AnyTypeKind.USER.name(), ldap.getKey()).fiql(
+                        "uid==pullFromLDAP").build(), pullTask);
+                fail("Should not arrive here");
+            } catch (SyncopeClientException sce) {
+                assertEquals(ClientExceptionType.Reconciliation, sce.getType());
+            }
+            Optional<RemediationTO> remediation = remediationService.list(
+                    new RemediationQuery.Builder().page(1).size(1000).build()).getResult().stream().
+                    filter(r -> "uid=pullFromLDAP,ou=People,o=isp".equalsIgnoreCase(r.getRemoteName())).
+                    findFirst();
+            assertTrue(remediation.isPresent());
+            assertEquals(AnyTypeKind.USER.name(), remediation.get().getAnyType());
+            assertEquals(ResourceOperation.CREATE, remediation.get().getOperation());
+            assertNotNull(remediation.get().getAnyCRPayload());
+            assertNull(remediation.get().getAnyURPayload());
+            assertNull(remediation.get().getKeyPayload());
+            assertTrue(remediation.get().getError().contains(
+                    "SyncopeClientCompositeException: {[RequiredValuesMissing [userId]]}"));
+        } finally {
+            resourceService.delete(ldap.getKey());
+            remediationService.list(new RemediationQuery.Builder().page(1).size(10).build()).getResult().forEach(
+                    r -> remediationService.delete(r.getKey()));
+        }
+    }
+
+    @Test
     public void issueSYNCOPE68() {
         //-----------------------------
         // Create a new user ... it should be updated applying pull policy