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 2019/11/04 15:32:31 UTC

[syncope] branch 2_1_X updated: [SYNCOPE-1508] PullActions and PropagationActions extended

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

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


The following commit(s) were added to refs/heads/2_1_X by this push:
     new e68e809  [SYNCOPE-1508] PullActions and PropagationActions extended
e68e809 is described below

commit e68e80927b5fcdb1721a235d1e3a5dce7e708dbc
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Mon Nov 4 15:08:06 2019 +0100

    [SYNCOPE-1508] PullActions and PropagationActions extended
---
 .../implementations/MyPropagationActions.groovy    | 14 +++++
 .../console/implementations/MyPullActions.groovy   | 13 +++++
 .../syncope/core/logic/ReconciliationLogic.java    |  4 +-
 .../apache/syncope/core/logic/ResourceLogic.java   |  4 +-
 .../api/propagation/PropagationActions.java        | 48 ++++++++++++++++
 .../provisioning/api/pushpull/PullActions.java     | 26 +++++++++
 .../AbstractPropagationTaskExecutor.java           | 50 ++++++++++-------
 .../pushpull/DefaultUserPushResultHandler.java     |  7 ++-
 .../java/pushpull/OutboundMatcher.java             | 64 ++++++++++++++++------
 .../java/pushpull/PullJobDelegate.java             | 15 ++++-
 .../java/pushpull/SinglePullJobDelegate.java       |  8 ++-
 11 files changed, 210 insertions(+), 43 deletions(-)

diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPropagationActions.groovy b/client/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPropagationActions.groovy
index 6a24777..6f58ce0 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPropagationActions.groovy
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPropagationActions.groovy
@@ -18,6 +18,10 @@
  * under the License.
  */
 import groovy.transform.CompileStatic
+import java.util.Collections
+import java.util.Set
+import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit
+import org.apache.syncope.core.persistence.api.entity.resource.Provision
 import org.apache.syncope.core.persistence.api.entity.task.PropagationTask
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec
 import org.apache.syncope.core.provisioning.api.propagation.PropagationActions
@@ -27,6 +31,16 @@ import org.identityconnectors.framework.common.objects.ConnectorObject
 class MyPropagationActions implements PropagationActions {
   
   @Override
+  Set<String> moreAttrsToGet(PropagationTask task, OrgUnit orgUnit) {
+    return Collections.emptySet();
+  }
+
+  @Override
+  Set<String> moreAttrsToGet(PropagationTask task, Provision provision) {
+    return Collections.emptySet();
+  }
+
+  @Override
   void before(PropagationTask task, ConnectorObject beforeObj) {
     // do nothing
   }
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPullActions.groovy b/client/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPullActions.groovy
index f7742a0..1020e68 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPullActions.groovy
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPullActions.groovy
@@ -17,8 +17,11 @@
  * under the License.
  */
 import groovy.transform.CompileStatic
+import java.util.Collections
 import org.apache.syncope.common.lib.patch.AnyPatch
 import org.apache.syncope.common.lib.to.EntityTO
+import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit
+import org.apache.syncope.core.persistence.api.entity.resource.Provision
 import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask
 import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningActions
@@ -30,6 +33,16 @@ import org.quartz.JobExecutionException
 
 @CompileStatic
 class MyPullActions implements PullActions {
+
+  @Override
+  Set<String> moreAttrsToGet(ProvisioningProfile profile, OrgUnit orgUnit) {
+    return Collections.emptySet();
+  }
+
+  @Override
+  Set<String> moreAttrsToGet(ProvisioningProfile profile, Provision provision) {
+    return Collections.emptySet();
+  }
   
   @Override
   SyncDelta preprocess(ProvisioningProfile profile, SyncDelta delta) {
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
index a3bff05..e8bbfcd 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
@@ -198,7 +198,9 @@ public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
                     connFactory.getConnector(provision.getResource()),
                     connObjectKeyItem,
                     query.getConnObjectKeyValue(),
-                    provision).
+                    provision,
+                    Optional.empty(),
+                    Optional.empty()).
                     ifPresent(connObj -> {
                         status.setOnResource(ConnObjectUtils.getConnObjectTO(connObj.getAttributes()));
 
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
index 55d05be..b888641 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
@@ -332,7 +332,9 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
                 connFactory.getConnector(provision.getResource()),
                 connObjectKeyItem,
                 connObjectKeyValue,
-                provision);
+                provision,
+                Optional.empty(),
+                Optional.empty());
         if (connObj.isPresent()) {
             return ConnObjectUtils.getConnObjectTO(connObj.get().getAttributes());
         }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationActions.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationActions.java
index d21edea..b08f507 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationActions.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationActions.java
@@ -18,20 +18,68 @@
  */
 package org.apache.syncope.core.provisioning.api.propagation;
 
+import java.util.Collections;
+import java.util.Set;
+import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
 
 public interface PropagationActions {
 
+    /**
+     * Return additional attributes to include in the result from the underlying connector.
+     *
+     * @param task propagation task
+     * @param orgUnit Realm provisioning information
+     * @return additional attributes to include in the result from the underlying connector
+     */
+    default Set<String> moreAttrsToGet(PropagationTask task, OrgUnit orgUnit) {
+        return Collections.emptySet();
+    }
+
+    /**
+     * Return additional attributes to include in the result from the underlying connector.
+     *
+     * @param task propagation task
+     * @param provision Any provisioning information
+     * @return additional attributes to include in the result from the underlying connector
+     */
+    default Set<String> moreAttrsToGet(PropagationTask task, Provision provision) {
+        return Collections.emptySet();
+    }
+
+    /**
+     * Executes logic before actual propagation.
+     *
+     * @param task propagation task
+     * @param beforeObj connector object read before propagation
+     */
     default void before(PropagationTask task, ConnectorObject beforeObj) {
         // do nothing
     }
 
+    /**
+     * Executes logic in case of propagation error.
+     * This method can throw {@link org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException} to
+     * ignore the reported error and continue.
+     *
+     * @param task propagation task
+     * @param execution execution result
+     * @param error propagation error
+     */
     default void onError(PropagationTask task, TaskExec execution, Exception error) {
         // do nothing
     }
 
+    /**
+     * Executes logic after actual propagation.
+     *
+     * @param task propagation task
+     * @param execution execution result
+     * @param afterObj connector object read after propagation
+     */
     default void after(PropagationTask task, TaskExec execution, ConnectorObject afterObj) {
         // do nothing
     }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/PullActions.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/PullActions.java
index d367a7e..ad95fde 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/PullActions.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/PullActions.java
@@ -18,8 +18,12 @@
  */
 package org.apache.syncope.core.provisioning.api.pushpull;
 
+import java.util.Collections;
+import java.util.Set;
 import org.apache.syncope.common.lib.patch.AnyPatch;
 import org.apache.syncope.common.lib.to.EntityTO;
+import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.identityconnectors.framework.common.objects.SyncDelta;
 import org.quartz.JobExecutionException;
 
@@ -31,6 +35,28 @@ import org.quartz.JobExecutionException;
 public interface PullActions extends ProvisioningActions {
 
     /**
+     * Return additional attributes to include in the result from the underlying connector.
+     *
+     * @param profile profile of the pull being executed.
+     * @param orgUnit Realm provisioning information
+     * @return additional attributes to include in the result from the underlying connector
+     */
+    default Set<String> moreAttrsToGet(ProvisioningProfile<?, ?> profile, OrgUnit orgUnit) {
+        return Collections.emptySet();
+    }
+
+    /**
+     * Return additional attributes to include in the result from the underlying connector.
+     *
+     * @param profile profile of the pull being executed.
+     * @param provision Any provisioning information
+     * @return additional attributes to include in the result from the underlying connector
+     */
+    default Set<String> moreAttrsToGet(ProvisioningProfile<?, ?> profile, Provision provision) {
+        return Collections.emptySet();
+    }
+
+    /**
      * Pre-process the pull information received by the underlying connector, before any internal activity occurs.
      *
      * @param profile profile of the pull being executed.
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
index 229aafe..504d0e2 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
@@ -301,28 +301,28 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
                 switch (task.getAnyTypeKind()) {
                     case USER:
                         try {
-                            resources = userDAO.findAllResourceKeys(task.getEntityKey());
-                        } catch (Exception e) {
-                            LOG.error("Could not read user {}", task.getEntityKey(), e);
-                        }
-                        break;
+                        resources = userDAO.findAllResourceKeys(task.getEntityKey());
+                    } catch (Exception e) {
+                        LOG.error("Could not read user {}", task.getEntityKey(), e);
+                    }
+                    break;
 
                     case GROUP:
                         try {
-                            resources = groupDAO.findAllResourceKeys(task.getEntityKey());
-                        } catch (Exception e) {
-                            LOG.error("Could not read group {}", task.getEntityKey(), e);
-                        }
-                        break;
+                        resources = groupDAO.findAllResourceKeys(task.getEntityKey());
+                    } catch (Exception e) {
+                        LOG.error("Could not read group {}", task.getEntityKey(), e);
+                    }
+                    break;
 
                     case ANY_OBJECT:
                     default:
                         try {
-                            resources = anyObjectDAO.findAllResourceKeys(task.getEntityKey());
-                        } catch (Exception e) {
-                            LOG.error("Could not read any object {}", task.getEntityKey(), e);
-                        }
-                        break;
+                        resources = anyObjectDAO.findAllResourceKeys(task.getEntityKey());
+                    } catch (Exception e) {
+                        LOG.error("Could not read any object {}", task.getEntityKey(), e);
+                    }
+                    break;
                 }
             }
             if (task.getAnyTypeKind() == null || !resources.contains(task.getResource().getKey())) {
@@ -398,8 +398,8 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
                 beforeObj = provision == null && orgUnit == null
                         ? null
                         : orgUnit == null
-                                ? getRemoteObject(task, connector, provision, false)
-                                : getRemoteObject(task, connector, orgUnit, false);
+                                ? getRemoteObject(task, connector, provision, actions, false)
+                                : getRemoteObject(task, connector, orgUnit, actions, false);
             } else if (taskInfo.getBeforeObj().isPresent()) {
                 beforeObj = taskInfo.getBeforeObj().get();
             }
@@ -468,8 +468,8 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
                     afterObj = provision == null && orgUnit == null
                             ? null
                             : orgUnit == null
-                                    ? getRemoteObject(task, connector, provision, true)
-                                    : getRemoteObject(task, connector, orgUnit, true);
+                                    ? getRemoteObject(task, connector, provision, actions, true)
+                                    : getRemoteObject(task, connector, orgUnit, actions, true);
                 } catch (Exception ignore) {
                     // ignore exception
                     LOG.error("Error retrieving after object", ignore);
@@ -612,6 +612,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
      * @param connector connector facade proxy.
      * @param task current propagation task.
      * @param provision provision
+     * @param actions propagation actions
      * @param latest 'FALSE' to retrieve object using old connObjectKey if not null.
      * @return remote connector object.
      */
@@ -619,13 +620,14 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
             final PropagationTask task,
             final Connector connector,
             final Provision provision,
+            final List<PropagationActions> actions,
             final boolean latest) {
 
         String connObjectKeyValue = latest || task.getOldConnObjectKey() == null
                 ? task.getConnObjectKey()
                 : task.getOldConnObjectKey();
 
-        List<ConnectorObject> matches = outboundMatcher.match(task, connector, provision, connObjectKeyValue);
+        List<ConnectorObject> matches = outboundMatcher.match(task, connector, provision, actions, connObjectKeyValue);
         LOG.debug("Found for propagation task {}: {}", task, matches);
 
         return matches.isEmpty() ? null : matches.get(0);
@@ -637,6 +639,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
      * @param connector connector facade proxy.
      * @param task current propagation task.
      * @param orgUnit orgUnit
+     * @param actions propagation actions
      * @param latest 'FALSE' to retrieve object using old connObjectKey if not null.
      * @return remote connector object.
      */
@@ -644,12 +647,16 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
             final PropagationTask task,
             final Connector connector,
             final OrgUnit orgUnit,
+            final List<PropagationActions> actions,
             final boolean latest) {
 
         String connObjectKey = latest || task.getOldConnObjectKey() == null
                 ? task.getConnObjectKey()
                 : task.getOldConnObjectKey();
 
+        Set<String> moreAttrsToGet = new HashSet<>();
+        actions.forEach(action -> moreAttrsToGet.addAll(action.moreAttrsToGet(task, orgUnit)));
+
         ConnectorObject obj = null;
         Optional<? extends OrgUnitItem> connObjectKeyItem = orgUnit.getConnObjectKeyItem();
         if (connObjectKeyItem.isPresent()) {
@@ -659,7 +666,8 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
                         AttributeBuilder.build(connObjectKeyItem.get().getExtAttrName(), connObjectKey),
                         orgUnit.isIgnoreCaseMatch(),
                         MappingUtils.buildOperationOptions(
-                                MappingUtils.getPropagationItems(orgUnit.getItems().stream())));
+                                MappingUtils.getPropagationItems(orgUnit.getItems().stream()),
+                                moreAttrsToGet.toArray(new String[0])));
             } catch (TimeoutException toe) {
                 LOG.debug("Request timeout", toe);
                 throw toe;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
index ad8fbb5..ba668a4 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
@@ -221,7 +221,12 @@ public class DefaultUserPushResultHandler extends AbstractPushResultHandler impl
         // Try to read remote object BEFORE any actual operation
         Optional<ConnectorObject> connObj = MappingUtils.getConnObjectKeyItem(provision).
                 map(connObjectKeyItem -> outboundMatcher.matchByConnObjectKeyValue(
-                profile.getConnector(), connObjectKeyItem, account.getConnObjectKeyValue(), provision)).
+                profile.getConnector(),
+                connObjectKeyItem,
+                account.getConnObjectKeyValue(),
+                provision,
+                Optional.empty(),
+                Optional.empty())).
                 orElse(Optional.empty());
         LOG.debug("Match found for linked account {} as {}: {}", account, provision.getObjectClass(), connObj);
 
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 e206ca8..6e98595 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,8 +19,12 @@
 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;
 import java.util.Optional;
+import java.util.Set;
 import java.util.stream.Stream;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
@@ -39,6 +43,7 @@ import org.apache.syncope.core.provisioning.api.Connector;
 import org.apache.syncope.core.provisioning.api.MappingManager;
 import org.apache.syncope.core.provisioning.api.TimeoutException;
 import org.apache.syncope.core.provisioning.api.VirAttrHandler;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
 import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
 import org.apache.syncope.core.spring.ImplementationManager;
 import org.identityconnectors.framework.common.objects.AttributeBuilder;
@@ -93,6 +98,7 @@ public class OutboundMatcher {
             final PropagationTask task,
             final Connector connector,
             final Provision provision,
+            final List<PropagationActions> actions,
             final String connObjectKeyValue) {
 
         Optional<PushCorrelationRule> rule = rule(provision);
@@ -104,14 +110,27 @@ public class OutboundMatcher {
             any = anyUtilsFactory.getInstance(task.getAnyTypeKind()).dao().find(task.getEntityKey());
         }
 
+        Set<String> moreAttrsToGet = new HashSet<>();
+        actions.forEach(action -> moreAttrsToGet.addAll(action.moreAttrsToGet(task, provision)));
+
         List<ConnectorObject> result = new ArrayList<>();
         try {
             if (any != null && rule.isPresent()) {
-                result.addAll(matchByCorrelationRule(connector, rule.get().getFilter(any, provision), provision));
+                result.addAll(matchByCorrelationRule(
+                        connector,
+                        rule.get().getFilter(any, provision),
+                        provision,
+                        Optional.empty(),
+                        Optional.of(moreAttrsToGet.toArray(new String[0]))));
             } else {
-                MappingUtils.getConnObjectKeyItem(provision).ifPresent(connObjectKeyItem
-                        -> matchByConnObjectKeyValue(connector, connObjectKeyItem, connObjectKeyValue, provision).
-                                ifPresent(result::add));
+                MappingUtils.getConnObjectKeyItem(provision).ifPresent(connObjectKeyItem -> matchByConnObjectKeyValue(
+                        connector,
+                        connObjectKeyItem,
+                        connObjectKeyValue,
+                        provision,
+                        Optional.empty(),
+                        Optional.of(moreAttrsToGet.toArray(new String[0]))).
+                        ifPresent(result::add));
             }
         } catch (RuntimeException e) {
             LOG.error("Could not match {} with any existing {}", any, provision.getObjectClass(), e);
@@ -137,14 +156,25 @@ public class OutboundMatcher {
         try {
             if (rule.isPresent()) {
                 result.addAll(matchByCorrelationRule(
-                        connector, rule.get().getFilter(any, provision), provision, linkingItems));
+                        connector,
+                        rule.get().getFilter(any, provision),
+                        provision,
+                        ArrayUtils.isEmpty(linkingItems)
+                        ? Optional.empty() : Optional.of(Arrays.asList(linkingItems)),
+                        Optional.empty()));
             } else {
                 Optional<? extends MappingItem> connObjectKeyItem = MappingUtils.getConnObjectKeyItem(provision);
                 Optional<String> connObjectKeyValue = mappingManager.getConnObjectKeyValue(any, provision);
 
                 if (connObjectKeyItem.isPresent() && connObjectKeyValue.isPresent()) {
                     matchByConnObjectKeyValue(
-                            connector, connObjectKeyItem.get(), connObjectKeyValue.get(), provision, linkingItems).
+                            connector,
+                            connObjectKeyItem.get(),
+                            connObjectKeyValue.get(),
+                            provision,
+                            ArrayUtils.isEmpty(linkingItems)
+                            ? Optional.empty() : Optional.of(Arrays.asList(linkingItems)),
+                            Optional.empty()).
                             ifPresent(result::add);
                 }
             }
@@ -163,13 +193,14 @@ public class OutboundMatcher {
             final Connector connector,
             final Filter filter,
             final Provision provision,
-            final LinkingMappingItem... linkingItems) {
+            final Optional<Collection<LinkingMappingItem>> linkingItems,
+            final Optional<String[]> moreAttrsToGet) {
 
         Stream<MappingItem> items = Stream.concat(
                 provision.getMapping().getItems().stream(),
-                ArrayUtils.isEmpty(linkingItems)
-                ? virSchemaDAO.findByProvision(provision).stream().map(VirSchema::asLinkingMappingItem)
-                : Stream.of(linkingItems));
+                linkingItems.isPresent()
+                ? linkingItems.get().stream()
+                : virSchemaDAO.findByProvision(provision).stream().map(VirSchema::asLinkingMappingItem));
 
         List<ConnectorObject> objs = new ArrayList<>();
         try {
@@ -185,7 +216,7 @@ public class OutboundMatcher {
                     objs.add(connectorObject);
                     return true;
                 }
-            }, MappingUtils.buildOperationOptions(items));
+            }, MappingUtils.buildOperationOptions(items, moreAttrsToGet.orElse(null)));
         } catch (TimeoutException toe) {
             LOG.debug("Request timeout", toe);
             throw toe;
@@ -202,13 +233,14 @@ public class OutboundMatcher {
             final MappingItem connObjectKeyItem,
             final String connObjectKeyValue,
             final Provision provision,
-            final LinkingMappingItem... linkingItems) {
+            final Optional<Collection<LinkingMappingItem>> linkingItems,
+            final Optional<String[]> moreAttrsToGet) {
 
         Stream<MappingItem> items = Stream.concat(
                 provision.getMapping().getItems().stream(),
-                ArrayUtils.isEmpty(linkingItems)
-                ? virSchemaDAO.findByProvision(provision).stream().map(VirSchema::asLinkingMappingItem)
-                : Stream.of(linkingItems));
+                linkingItems.isPresent()
+                ? linkingItems.get().stream()
+                : virSchemaDAO.findByProvision(provision).stream().map(VirSchema::asLinkingMappingItem));
 
         ConnectorObject obj = null;
         try {
@@ -216,7 +248,7 @@ public class OutboundMatcher {
                     provision.getObjectClass(),
                     AttributeBuilder.build(connObjectKeyItem.getExtAttrName(), connObjectKeyValue),
                     provision.isIgnoreCaseMatch(),
-                    MappingUtils.buildOperationOptions(items));
+                    MappingUtils.buildOperationOptions(items, moreAttrsToGet.orElse(null)));
         } catch (TimeoutException toe) {
             LOG.debug("Request timeout", toe);
             throw toe;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
index 8362e69..a03a06e 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
@@ -20,9 +20,11 @@ package org.apache.syncope.core.provisioning.java.pushpull;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
 import java.util.stream.Stream;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.types.ConflictResolutionAction;
@@ -219,8 +221,12 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
             status.set("Pulling " + pullTask.getResource().getOrgUnit().getObjectClass().getObjectClassValue());
 
             OrgUnit orgUnit = pullTask.getResource().getOrgUnit();
+
+            Set<String> moreAttrsToGet = new HashSet<>();
+            actions.forEach(action -> moreAttrsToGet.addAll(action.moreAttrsToGet(profile, orgUnit)));
+
             OperationOptions options = MappingUtils.buildOperationOptions(
-                    MappingUtils.getPullItems(orgUnit.getItems().stream()));
+                    MappingUtils.getPullItems(orgUnit.getItems().stream()), moreAttrsToGet.toArray(new String[0]));
 
             RealmPullResultHandler handler = buildRealmHandler();
             handler.setProfile(profile);
@@ -290,10 +296,15 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
                 handler.setPullExecutor(this);
 
                 try {
+                    Set<String> moreAttrsToGet = new HashSet<>();
+                    actions.forEach(action -> moreAttrsToGet.addAll(action.moreAttrsToGet(profile, provision)));
+
                     Stream<? extends Item> mapItems = Stream.concat(
                             MappingUtils.getPullItems(provision.getMapping().getItems().stream()),
                             virSchemaDAO.findByProvision(provision).stream().map(VirSchema::asLinkingMappingItem));
-                    OperationOptions options = MappingUtils.buildOperationOptions(mapItems);
+
+                    OperationOptions options = MappingUtils.buildOperationOptions(
+                            mapItems, moreAttrsToGet.toArray(new String[0]));
 
                     switch (pullTask.getPullMode()) {
                         case INCREMENTAL:
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
index 9be230b..deebedf 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
@@ -19,7 +19,9 @@
 package org.apache.syncope.core.provisioning.java.pushpull;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.stream.Stream;
 import org.apache.syncope.common.lib.to.PullTaskTO;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
@@ -152,14 +154,18 @@ public class SinglePullJobDelegate extends PullJobDelegate implements SyncopeSin
             handler.setPullExecutor(this);
 
             // execute filtered pull
+            Set<String> moreAttrsToGet = new HashSet<>();
+            actions.forEach(action -> moreAttrsToGet.addAll(action.moreAttrsToGet(profile, provision)));
+
             Stream<? extends Item> mapItems = Stream.concat(
                     MappingUtils.getPullItems(provision.getMapping().getItems().stream()),
                     virSchemaDAO.findByProvision(provision).stream().map(VirSchema::asLinkingMappingItem));
+
             connector.filteredReconciliation(
                     provision.getObjectClass(),
                     new AccountReconciliationFilterBuilder(connObjectKey, connObjectValue),
                     handler,
-                    MappingUtils.buildOperationOptions(mapItems));
+                    MappingUtils.buildOperationOptions(mapItems, moreAttrsToGet.toArray(new String[0])));
 
             try {
                 setGroupOwners(ghandler);