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 2016/03/11 14:45:55 UTC

[07/13] syncope git commit: [SYNCOPE-771] Rename completed

http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
new file mode 100644
index 0000000..e2570eb
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
@@ -0,0 +1,434 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.apache.commons.collections4.IteratorUtils;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.apache.syncope.common.lib.patch.AnyPatch;
+import org.apache.syncope.common.lib.patch.StringPatchItem;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditElements.Result;
+import org.apache.syncope.common.lib.types.MatchingRule;
+import org.apache.syncope.common.lib.types.PatchOperation;
+import org.apache.syncope.common.lib.types.PropagationByResource;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.common.lib.types.UnmatchingRule;
+import org.apache.syncope.core.persistence.api.entity.task.PushTask;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
+import org.apache.syncope.core.provisioning.java.MappingManagerImpl;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+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.provisioning.api.MappingManager;
+import org.apache.syncope.core.provisioning.api.TimeoutException;
+import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
+import org.apache.syncope.core.provisioning.api.pushpull.SyncopePushResultHandler;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.Uid;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHandler<PushTask, PushActions>
+        implements SyncopePushResultHandler {
+
+    @Autowired
+    protected MappingManager mappingManager;
+
+    protected abstract String getName(Any<?> any);
+
+    protected void deprovision(final Any<?> any) {
+        AnyTO before = getAnyTO(any.getKey());
+
+        List<String> noPropResources = new ArrayList<>(before.getResources());
+        noPropResources.remove(profile.getTask().getResource().getKey());
+
+        taskExecutor.execute(propagationManager.getDeleteTasks(
+                any.getType().getKind(),
+                any.getKey(),
+                null,
+                noPropResources));
+    }
+
+    protected void provision(final Any<?> any, final Boolean enabled) {
+        AnyTO before = getAnyTO(any.getKey());
+
+        List<String> noPropResources = new ArrayList<>(before.getResources());
+        noPropResources.remove(profile.getTask().getResource().getKey());
+
+        PropagationByResource propByRes = new PropagationByResource();
+        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
+
+        taskExecutor.execute(propagationManager.getCreateTasks(
+                any.getType().getKind(),
+                any.getKey(),
+                propByRes,
+                before.getVirAttrs(),
+                noPropResources));
+    }
+
+    @SuppressWarnings("unchecked")
+    protected void link(final Any<?> any, final Boolean unlink) {
+        AnyPatch patch = newPatch(any.getKey());
+        patch.getResources().add(new StringPatchItem.Builder().
+                operation(unlink ? PatchOperation.DELETE : PatchOperation.ADD_REPLACE).
+                value(profile.getTask().getResource().getKey()).build());
+
+        update(patch);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected void unassign(final Any<?> any) {
+        AnyPatch patch = newPatch(any.getKey());
+        patch.getResources().add(new StringPatchItem.Builder().
+                operation(PatchOperation.DELETE).
+                value(profile.getTask().getResource().getKey()).build());
+
+        update(patch);
+
+        deprovision(any);
+    }
+
+    protected void assign(final Any<?> any, final Boolean enabled) {
+        AnyPatch patch = newPatch(any.getKey());
+        patch.getResources().add(new StringPatchItem.Builder().
+                operation(PatchOperation.ADD_REPLACE).
+                value(profile.getTask().getResource().getKey()).build());
+
+        update(patch);
+
+        provision(any, enabled);
+    }
+
+    protected ConnectorObject getRemoteObject(final String connObjectKey, final ObjectClass objectClass) {
+        ConnectorObject obj = null;
+        try {
+            Uid uid = new Uid(connObjectKey);
+
+            obj = profile.getConnector().getObject(objectClass,
+                    uid,
+                    MappingManagerImpl.buildOperationOptions(IteratorUtils.<MappingItem>emptyIterator()));
+        } catch (TimeoutException toe) {
+            LOG.debug("Request timeout", toe);
+            throw toe;
+        } catch (RuntimeException ignore) {
+            LOG.debug("While resolving {}", connObjectKey, ignore);
+        }
+
+        return obj;
+    }
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
+    @Override
+    public boolean handle(final long anyKey) {
+        Any<?> any = null;
+        try {
+            any = getAny(anyKey);
+            doHandle(any);
+            return true;
+        } catch (IgnoreProvisionException e) {
+            ProvisioningReport result = new ProvisioningReport();
+            result.setOperation(ResourceOperation.NONE);
+            result.setAnyType(any == null ? null : any.getType().getKey());
+            result.setStatus(ProvisioningReport.Status.IGNORE);
+            result.setKey(anyKey);
+            profile.getResults().add(result);
+
+            LOG.warn("Ignoring during push", e);
+            return true;
+        } catch (JobExecutionException e) {
+            LOG.error("Push failed", e);
+            return false;
+        }
+    }
+
+    protected final void doHandle(final Any<?> any) throws JobExecutionException {
+        AnyUtils anyUtils = anyUtilsFactory.getInstance(any);
+
+        ProvisioningReport result = new ProvisioningReport();
+        profile.getResults().add(result);
+
+        result.setKey(any.getKey());
+        result.setAnyType(any.getType().getKey());
+        result.setName(getName(any));
+
+        Boolean enabled = any instanceof User && profile.getTask().isPullStatus()
+                ? ((User) any).isSuspended() ? Boolean.FALSE : Boolean.TRUE
+                : null;
+
+        LOG.debug("Propagating {} with key {} towards {}",
+                anyUtils.getAnyTypeKind(), any.getKey(), profile.getTask().getResource());
+
+        Object output = null;
+        Result resultStatus = null;
+        String operation = null;
+
+        // Try to read remote object BEFORE any actual operation
+        Provision provision = profile.getTask().getResource().getProvision(any.getType());
+        String connObjecKey = mappingManager.getConnObjectKeyValue(any, provision);
+
+        ConnectorObject beforeObj = getRemoteObject(connObjecKey, provision.getObjectClass());
+
+        Boolean status = profile.getTask().isPullStatus() ? enabled : null;
+
+        if (profile.isDryRun()) {
+            if (beforeObj == null) {
+                result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule()));
+            } else {
+                result.setOperation(getResourceOperation(profile.getTask().getMatchingRule()));
+            }
+            result.setStatus(ProvisioningReport.Status.SUCCESS);
+        } else {
+            try {
+                if (beforeObj == null) {
+                    operation = UnmatchingRule.toEventName(profile.getTask().getUnmatchingRule());
+                    result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule()));
+
+                    switch (profile.getTask().getUnmatchingRule()) {
+                        case ASSIGN:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeAssign(this.getProfile(), any);
+                            }
+
+                            if (!profile.getTask().isPerformCreate()) {
+                                LOG.debug("PushTask not configured for create");
+                            } else {
+                                assign(any, status);
+                            }
+
+                            break;
+
+                        case PROVISION:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeProvision(this.getProfile(), any);
+                            }
+
+                            if (!profile.getTask().isPerformCreate()) {
+                                LOG.debug("PushTask not configured for create");
+                            } else {
+                                provision(any, status);
+                            }
+
+                            break;
+
+                        case UNLINK:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeUnlink(this.getProfile(), any);
+                            }
+
+                            if (!profile.getTask().isPerformUpdate()) {
+                                LOG.debug("PushTask not configured for update");
+                            } else {
+                                link(any, true);
+                            }
+
+                            break;
+
+                        case IGNORE:
+                            LOG.debug("Ignored any: {}", any);
+                            break;
+                        default:
+                        // do nothing
+                    }
+                } else {
+                    operation = MatchingRule.toEventName(profile.getTask().getMatchingRule());
+                    result.setOperation(getResourceOperation(profile.getTask().getMatchingRule()));
+
+                    switch (profile.getTask().getMatchingRule()) {
+                        case UPDATE:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeUpdate(this.getProfile(), any);
+                            }
+                            if (!profile.getTask().isPerformUpdate()) {
+                                LOG.debug("PushTask not configured for update");
+                            } else {
+                                update(any, status);
+                            }
+
+                            break;
+
+                        case DEPROVISION:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeDeprovision(this.getProfile(), any);
+                            }
+
+                            if (!profile.getTask().isPerformDelete()) {
+                                LOG.debug("PushTask not configured for delete");
+                            } else {
+                                deprovision(any);
+                            }
+
+                            break;
+
+                        case UNASSIGN:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeUnassign(this.getProfile(), any);
+                            }
+
+                            if (!profile.getTask().isPerformDelete()) {
+                                LOG.debug("PushTask not configured for delete");
+                            } else {
+                                unassign(any);
+                            }
+
+                            break;
+
+                        case LINK:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeLink(this.getProfile(), any);
+                            }
+
+                            if (!profile.getTask().isPerformUpdate()) {
+                                LOG.debug("PushTask not configured for update");
+                            } else {
+                                link(any, false);
+                            }
+
+                            break;
+
+                        case UNLINK:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeUnlink(this.getProfile(), any);
+                            }
+
+                            if (!profile.getTask().isPerformUpdate()) {
+                                LOG.debug("PushTask not configured for update");
+                            } else {
+                                link(any, true);
+                            }
+
+                            break;
+
+                        case IGNORE:
+                            LOG.debug("Ignored any: {}", any);
+                            break;
+                        default:
+                        // do nothing
+                    }
+                }
+
+                for (PushActions action : profile.getActions()) {
+                    action.after(this.getProfile(), any, result);
+                }
+
+                result.setStatus(ProvisioningReport.Status.SUCCESS);
+                resultStatus = AuditElements.Result.SUCCESS;
+                output = getRemoteObject(connObjecKey, provision.getObjectClass());
+            } catch (IgnoreProvisionException e) {
+                throw e;
+            } catch (Exception e) {
+                result.setStatus(ProvisioningReport.Status.FAILURE);
+                result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+                resultStatus = AuditElements.Result.FAILURE;
+                output = e;
+
+                LOG.warn("Error pushing {} towards {}", any, profile.getTask().getResource(), e);
+
+                for (PushActions action : profile.getActions()) {
+                    action.onError(this.getProfile(), any, result, e);
+                }
+
+                throw new JobExecutionException(e);
+            } finally {
+                notificationManager.createTasks(AuditElements.EventCategoryType.PUSH,
+                        any.getType().getKind().name().toLowerCase(),
+                        profile.getTask().getResource().getKey(),
+                        operation,
+                        resultStatus,
+                        beforeObj,
+                        output,
+                        any);
+                auditManager.audit(AuditElements.EventCategoryType.PUSH,
+                        any.getType().getKind().name().toLowerCase(),
+                        profile.getTask().getResource().getKey(),
+                        operation,
+                        resultStatus,
+                        connObjectUtils.getConnObjectTO(beforeObj),
+                        output instanceof ConnectorObject
+                                ? connObjectUtils.getConnObjectTO((ConnectorObject) output) : output,
+                        any);
+            }
+        }
+    }
+
+    private ResourceOperation getResourceOperation(final UnmatchingRule rule) {
+        switch (rule) {
+            case ASSIGN:
+            case PROVISION:
+                return ResourceOperation.CREATE;
+            default:
+                return ResourceOperation.NONE;
+        }
+    }
+
+    private ResourceOperation getResourceOperation(final MatchingRule rule) {
+        switch (rule) {
+            case UPDATE:
+                return ResourceOperation.UPDATE;
+            case DEPROVISION:
+            case UNASSIGN:
+                return ResourceOperation.DELETE;
+            default:
+                return ResourceOperation.NONE;
+        }
+    }
+
+    protected Any<?> update(final Any<?> any, final Boolean enabled) {
+        boolean changepwd;
+        Collection<String> resourceNames;
+        if (any instanceof User) {
+            changepwd = true;
+            resourceNames = userDAO.findAllResourceNames((User) any);
+        } else if (any instanceof AnyObject) {
+            changepwd = false;
+            resourceNames = anyObjectDAO.findAllResourceNames((AnyObject) any);
+        } else {
+            changepwd = false;
+            resourceNames = ((Group) any).getResourceNames();
+        }
+
+        List<String> noPropResources = new ArrayList<>(resourceNames);
+        noPropResources.remove(profile.getTask().getResource().getKey());
+
+        PropagationByResource propByRes = new PropagationByResource();
+        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
+
+        taskExecutor.execute(propagationManager.getUpdateTasks(
+                any.getType().getKind(),
+                any.getKey(),
+                changepwd,
+                null,
+                propByRes,
+                null,
+                noPropResources));
+
+        return getAny(any.getKey());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
new file mode 100644
index 0000000..37f67ff
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
@@ -0,0 +1,155 @@
+/*
+ * 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 org.apache.syncope.common.lib.patch.AnyPatch;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
+import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
+import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
+import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
+import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
+import org.apache.syncope.core.provisioning.api.pushpull.SyncopeResultHandler;
+import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
+import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
+import org.apache.syncope.core.provisioning.api.AuditManager;
+import org.apache.syncope.core.provisioning.api.WorkflowResult;
+import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
+import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningActions;
+import org.apache.syncope.core.workflow.api.AnyObjectWorkflowAdapter;
+import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
+import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public abstract class AbstractSyncopeResultHandler<T extends ProvisioningTask, A extends ProvisioningActions>
+        implements SyncopeResultHandler<T, A> {
+
+    protected static final Logger LOG = LoggerFactory.getLogger(SyncopeResultHandler.class);
+
+    @Autowired
+    protected AnyObjectDAO anyObjectDAO;
+
+    @Autowired
+    protected UserDAO userDAO;
+
+    @Autowired
+    protected GroupDAO groupDAO;
+
+    /**
+     * ConnectorObject utils.
+     */
+    @Autowired
+    protected ConnObjectUtils connObjectUtils;
+
+    /**
+     * Notification Manager.
+     */
+    @Autowired
+    protected NotificationManager notificationManager;
+
+    /**
+     * Audit Manager.
+     */
+    @Autowired
+    protected AuditManager auditManager;
+
+    /**
+     * Propagation manager.
+     */
+    @Autowired
+    protected PropagationManager propagationManager;
+
+    /**
+     * Task executor.
+     */
+    @Autowired
+    protected PropagationTaskExecutor taskExecutor;
+
+    protected AnyObjectWorkflowAdapter awfAdapter;
+
+    /**
+     * User workflow adapter.
+     */
+    @Autowired
+    protected UserWorkflowAdapter uwfAdapter;
+
+    /**
+     * Group workflow adapter.
+     */
+    @Autowired
+    protected GroupWorkflowAdapter gwfAdapter;
+
+    @Autowired
+    protected AnyObjectDataBinder anyObjectDataBinder;
+
+    @Autowired
+    protected UserDataBinder userDataBinder;
+
+    @Autowired
+    protected GroupDataBinder groupDataBinder;
+
+    @Autowired
+    protected AnyObjectProvisioningManager anyObjectProvisioningManager;
+
+    @Autowired
+    protected UserProvisioningManager userProvisioningManager;
+
+    @Autowired
+    protected GroupProvisioningManager groupProvisioningManager;
+
+    @Autowired
+    protected AnyUtilsFactory anyUtilsFactory;
+
+    /**
+     * Sync profile.
+     */
+    protected ProvisioningProfile<T, A> profile;
+
+    protected abstract AnyUtils getAnyUtils();
+
+    protected abstract AnyTO getAnyTO(long key);
+
+    protected abstract Any<?> getAny(long key);
+
+    protected abstract AnyPatch newPatch(final long key);
+
+    protected abstract WorkflowResult<Long> update(AnyPatch patch);
+
+    @Override
+    public void setProfile(final ProvisioningProfile<T, A> profile) {
+        this.profile = profile;
+    }
+
+    @Override
+    public ProvisioningProfile<T, A> getProfile() {
+        return profile;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
new file mode 100644
index 0000000..67c5731
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
@@ -0,0 +1,112 @@
+/*
+ * 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 java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.patch.AnyObjectPatch;
+import org.apache.syncope.common.lib.patch.AnyPatch;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.PropagationStatus;
+import org.apache.syncope.common.lib.to.AnyObjectTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.provisioning.api.ProvisioningManager;
+import org.apache.syncope.core.provisioning.api.WorkflowResult;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPullResultHandler;
+
+public class AnyObjectPullResultHandlerImpl extends AbstractPullResultHandler implements AnyObjectPullResultHandler {
+
+    @Override
+    protected AnyUtils getAnyUtils() {
+        return anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT);
+    }
+
+    @Override
+    protected String getName(final AnyTO anyTO) {
+        return StringUtils.EMPTY;
+    }
+
+    @Override
+    protected ProvisioningManager<?, ?> getProvisioningManager() {
+        return anyObjectProvisioningManager;
+    }
+
+    @Override
+    protected Any<?> getAny(final long key) {
+        try {
+            return anyObjectDAO.authFind(key);
+        } catch (Exception e) {
+            LOG.warn("Error retrieving anyObject {}", key, e);
+            return null;
+        }
+    }
+
+    @Override
+    protected AnyTO getAnyTO(final long key) {
+        return anyObjectDataBinder.getAnyObjectTO(key);
+    }
+
+    @Override
+    protected AnyPatch newPatch(final long key) {
+        AnyObjectPatch patch = new AnyObjectPatch();
+        patch.setKey(key);
+        return patch;
+    }
+
+    @Override
+    protected WorkflowResult<Long> update(final AnyPatch patch) {
+        return awfAdapter.update((AnyObjectPatch) patch);
+    }
+
+    @Override
+    protected AnyTO doCreate(final AnyTO anyTO, final SyncDelta delta, final ProvisioningReport result) {
+        AnyObjectTO anyObjectTO = AnyObjectTO.class.cast(anyTO);
+
+        Map.Entry<Long, List<PropagationStatus>> created = anyObjectProvisioningManager.create(
+                anyObjectTO, Collections.singleton(profile.getTask().getResource().getKey()), true);
+
+        result.setKey(created.getKey());
+        result.setName(getName(anyTO));
+
+        return getAnyTO(created.getKey());
+    }
+
+    @Override
+    protected AnyTO doUpdate(
+            final AnyTO before,
+            final AnyPatch anyPatch,
+            final SyncDelta delta,
+            final ProvisioningReport result) {
+
+        AnyObjectPatch anyObjectPatch = AnyObjectPatch.class.cast(anyPatch);
+
+        Map.Entry<Long, List<PropagationStatus>> updated =
+                anyObjectProvisioningManager.update(anyObjectPatch, true);
+
+        AnyObjectTO after = anyObjectDataBinder.getAnyObjectTO(updated.getKey());
+        result.setName(getName(after));
+        return after;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPushResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPushResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPushResultHandlerImpl.java
new file mode 100644
index 0000000..ce5aebb
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPushResultHandlerImpl.java
@@ -0,0 +1,70 @@
+/*
+ * 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 org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.patch.AnyObjectPatch;
+import org.apache.syncope.common.lib.patch.AnyPatch;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.provisioning.api.WorkflowResult;
+import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPushResultHandler;
+
+public class AnyObjectPushResultHandlerImpl extends AbstractPushResultHandler implements AnyObjectPushResultHandler {
+
+    @Override
+    protected AnyUtils getAnyUtils() {
+        return anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT);
+    }
+
+    @Override
+    protected String getName(final Any<?> any) {
+        return StringUtils.EMPTY;
+    }
+
+    @Override
+    protected Any<?> getAny(final long key) {
+        try {
+            return anyObjectDAO.authFind(key);
+        } catch (Exception e) {
+            LOG.warn("Error retrieving anyObject {}", key, e);
+            return null;
+        }
+    }
+
+    @Override
+    protected AnyTO getAnyTO(final long key) {
+        return anyObjectDataBinder.getAnyObjectTO(key);
+    }
+
+    @Override
+    protected AnyPatch newPatch(final long key) {
+        AnyObjectPatch patch = new AnyObjectPatch();
+        patch.setKey(key);
+        return patch;
+    }
+
+    @Override
+    protected WorkflowResult<Long> update(final AnyPatch patch) {
+        return awfAdapter.update((AnyObjectPatch) patch);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActions.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActions.java
new file mode 100644
index 0000000..2df5ace
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActions.java
@@ -0,0 +1,142 @@
+/*
+ * 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 org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
+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.AnyTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.CipherAlgorithm;
+import org.apache.syncope.common.lib.types.ConnConfProperty;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.ConnInstance;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.Connector;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * A {@link org.apache.syncope.core.provisioning.api.pushpull.PullActions} implementation which allows the ability to
+ * import passwords from a Database backend, where the passwords are hashed according to the password cipher algorithm
+ * property of the (DB) Connector and HEX-encoded.
+ */
+public class DBPasswordPullActions extends DefaultPullActions {
+
+    private static final Logger LOG = LoggerFactory.getLogger(DBPasswordPullActions.class);
+
+    private static final String CLEARTEXT = "CLEARTEXT";
+
+    @Autowired
+    private UserDAO userDAO;
+
+    private String encodedPassword;
+
+    private CipherAlgorithm cipher;
+
+    @Transactional(readOnly = true)
+    @Override
+    public <A extends AnyTO> SyncDelta beforeProvision(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final A any) throws JobExecutionException {
+
+        if (any instanceof UserTO) {
+            String password = ((UserTO) any).getPassword();
+            parseEncodedPassword(password, profile.getConnector());
+        }
+
+        return delta;
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public <A extends AnyTO, M extends AnyPatch> SyncDelta beforeUpdate(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final A any,
+            final M anyPatch) throws JobExecutionException {
+
+        if (anyPatch instanceof UserPatch) {
+            PasswordPatch modPassword = ((UserPatch) anyPatch).getPassword();
+            parseEncodedPassword(modPassword == null ? null : modPassword.getValue(), profile.getConnector());
+        }
+
+        return delta;
+    }
+
+    private void parseEncodedPassword(final String password, final Connector connector) {
+        if (password != null) {
+            ConnInstance connInstance = connector.getConnInstance();
+
+            String cipherAlgorithm = getCipherAlgorithm(connInstance);
+            if (!CLEARTEXT.equals(cipherAlgorithm)) {
+                try {
+                    encodedPassword = password;
+                    cipher = CipherAlgorithm.valueOf(cipherAlgorithm);
+                } catch (IllegalArgumentException e) {
+                    LOG.error("Cipher algorithm not allowed: {}", cipherAlgorithm, e);
+                    encodedPassword = null;
+                }
+            }
+        }
+    }
+
+    private String getCipherAlgorithm(final ConnInstance connInstance) {
+        ConnConfProperty cipherAlgorithm =
+                IterableUtils.find(connInstance.getConf(), new Predicate<ConnConfProperty>() {
+
+                    @Override
+                    public boolean evaluate(final ConnConfProperty property) {
+                        return "cipherAlgorithm".equals(property.getSchema().getName())
+                                && property.getValues() != null && !property.getValues().isEmpty();
+                    }
+                });
+
+        return cipherAlgorithm == null
+                ? CLEARTEXT
+                : (String) cipherAlgorithm.getValues().get(0);
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public <A extends AnyTO> void after(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final A any,
+            final ProvisioningReport result) throws JobExecutionException {
+
+        if (any instanceof UserTO && encodedPassword != null && cipher != null) {
+            User syncopeUser = userDAO.find(any.getKey());
+            if (syncopeUser != null) {
+                syncopeUser.setEncodedPassword(encodedPassword.toUpperCase(), cipher);
+            }
+            encodedPassword = null;
+            cipher = null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultPullActions.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultPullActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultPullActions.java
new file mode 100644
index 0000000..7a4cbe0
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultPullActions.java
@@ -0,0 +1,121 @@
+/*
+ * 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 org.apache.syncope.common.lib.patch.AnyPatch;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+import org.quartz.JobExecutionException;
+import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
+
+/**
+ * Default (empty) implementation of {@link PullActions}.
+ */
+public abstract class DefaultPullActions implements PullActions {
+
+    @Override
+    public void beforeAll(final ProvisioningProfile<?, ?> profile) throws JobExecutionException {
+    }
+
+    @Override
+    public <A extends AnyTO, P extends AnyPatch> SyncDelta beforeUpdate(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final A any,
+            final P anyMod) throws JobExecutionException {
+
+        return delta;
+    }
+
+    @Override
+    public <A extends AnyTO> SyncDelta beforeDelete(
+            final ProvisioningProfile<?, ?> profile, final SyncDelta delta, final A any)
+            throws JobExecutionException {
+
+        return delta;
+    }
+
+    @Override
+    public <A extends AnyTO> SyncDelta beforeAssign(
+            final ProvisioningProfile<?, ?> profile, final SyncDelta delta, final A any)
+            throws JobExecutionException {
+
+        return delta;
+    }
+
+    @Override
+    public <A extends AnyTO> SyncDelta beforeProvision(
+            final ProvisioningProfile<?, ?> profile, final SyncDelta delta, final A any)
+            throws JobExecutionException {
+
+        return delta;
+    }
+
+    @Override
+    public <A extends AnyTO> SyncDelta beforeLink(
+            final ProvisioningProfile<?, ?> profile, final SyncDelta delta, final A any)
+            throws JobExecutionException {
+
+        return delta;
+    }
+
+    @Override
+    public <A extends AnyTO> SyncDelta beforeUnassign(
+            final ProvisioningProfile<?, ?> profile, final SyncDelta delta, final A any)
+            throws JobExecutionException {
+
+        return delta;
+    }
+
+    @Override
+    public <A extends AnyTO> SyncDelta beforeDeprovision(
+            final ProvisioningProfile<?, ?> profile, final SyncDelta delta, final A any)
+            throws JobExecutionException {
+
+        return delta;
+    }
+
+    @Override
+    public <A extends AnyTO> SyncDelta beforeUnlink(
+            final ProvisioningProfile<?, ?> profile, final SyncDelta delta, final A any)
+            throws JobExecutionException {
+
+        return delta;
+    }
+
+    @Override
+    public void onError(
+            final ProvisioningProfile<?, ?> profile, final SyncDelta delta, final ProvisioningReport result,
+            final Exception error) throws JobExecutionException {
+    }
+
+    @Override
+    public <A extends AnyTO> void after(
+            final ProvisioningProfile<?, ?> profile, final SyncDelta delta, final A any,
+            final ProvisioningReport result)
+            throws JobExecutionException {
+    }
+
+    @Override
+    public void afterAll(final ProvisioningProfile<?, ?> profile)
+            throws JobExecutionException {
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultPushActions.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultPushActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultPushActions.java
new file mode 100644
index 0000000..2e75870
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultPushActions.java
@@ -0,0 +1,100 @@
+/*
+ * 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 org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.quartz.JobExecutionException;
+
+/**
+ * Default (empty) implementation of PushActions.
+ */
+public abstract class DefaultPushActions implements PushActions {
+
+    @Override
+    public void beforeAll(final ProvisioningProfile<?, ?> profile) throws JobExecutionException {
+    }
+
+    @Override
+    public <A extends Any<?>> A beforeAssign(final ProvisioningProfile<?, ?> profile, final A any)
+            throws JobExecutionException {
+
+        return any;
+    }
+
+    @Override
+    public <A extends Any<?>> A beforeProvision(final ProvisioningProfile<?, ?> profile, final A any)
+            throws JobExecutionException {
+
+        return any;
+    }
+
+    @Override
+    public <A extends Any<?>> A beforeLink(final ProvisioningProfile<?, ?> profile, final A any)
+            throws JobExecutionException {
+
+        return any;
+    }
+
+    @Override
+    public <A extends Any<?>> A beforeUnassign(final ProvisioningProfile<?, ?> profile, final A any)
+            throws JobExecutionException {
+
+        return any;
+    }
+
+    @Override
+    public <A extends Any<?>> A beforeDeprovision(final ProvisioningProfile<?, ?> profile, final A any)
+            throws JobExecutionException {
+
+        return any;
+    }
+
+    @Override
+    public <A extends Any<?>> A beforeUnlink(final ProvisioningProfile<?, ?> profile, final A any)
+            throws JobExecutionException {
+
+        return any;
+    }
+
+    @Override
+    public <A extends Any<?>> void onError(
+            final ProvisioningProfile<?, ?> profile, final A any, final ProvisioningReport result,
+            final Exception error) throws JobExecutionException {
+
+        // do nothing
+    }
+
+    @Override
+    public <A extends Any<?>> void after(
+            final ProvisioningProfile<?, ?> profile, final A any, final ProvisioningReport result)
+            throws JobExecutionException {
+
+        // do nothing
+    }
+
+    @Override
+    public void afterAll(final ProvisioningProfile<?, ?> profile)
+            throws JobExecutionException {
+
+        // do nothing
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultReconciliationFilterBuilder.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultReconciliationFilterBuilder.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultReconciliationFilterBuilder.java
new file mode 100644
index 0000000..c46c0fb
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultReconciliationFilterBuilder.java
@@ -0,0 +1,38 @@
+/*
+ * 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.identityconnectors.framework.impl.api.local.operations.FilteredResultsHandler.PassThroughFilter;
+
+import org.identityconnectors.framework.common.objects.filter.Filter;
+import org.apache.syncope.core.provisioning.api.pushpull.ReconciliationFilterBuilder;
+
+/**
+ * Default (pass-through) implementation of {@link ReconciliationFilterBuilder}.
+ */
+public abstract class DefaultReconciliationFilterBuilder implements ReconciliationFilterBuilder {
+
+    private static final PassThroughFilter PASS_THROUGH = new PassThroughFilter();
+
+    @Override
+    public Filter build() {
+        return PASS_THROUGH;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
new file mode 100644
index 0000000..b880b57
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
@@ -0,0 +1,138 @@
+/*
+ * 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 java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.syncope.common.lib.patch.AnyPatch;
+import org.apache.syncope.common.lib.patch.AttrPatch;
+import org.apache.syncope.common.lib.patch.GroupPatch;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.PropagationStatus;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.PatchOperation;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.provisioning.api.ProvisioningManager;
+import org.apache.syncope.core.provisioning.api.WorkflowResult;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+import org.apache.syncope.core.provisioning.api.pushpull.GroupPullResultHandler;
+
+public class GroupPullResultHandlerImpl extends AbstractPullResultHandler implements GroupPullResultHandler {
+
+    protected final Map<Long, String> groupOwnerMap = new HashMap<>();
+
+    @Override
+    public Map<Long, String> getGroupOwnerMap() {
+        return this.groupOwnerMap;
+    }
+
+    @Override
+    protected AnyUtils getAnyUtils() {
+        return anyUtilsFactory.getInstance(AnyTypeKind.GROUP);
+    }
+
+    @Override
+    protected String getName(final AnyTO anyTO) {
+        return GroupTO.class.cast(anyTO).getName();
+    }
+
+    @Override
+    protected ProvisioningManager<?, ?> getProvisioningManager() {
+        return groupProvisioningManager;
+    }
+
+    @Override
+    protected Any<?> getAny(final long key) {
+        try {
+            return groupDAO.authFind(key);
+        } catch (Exception e) {
+            LOG.warn("Error retrieving group {}", key, e);
+            return null;
+        }
+    }
+
+    @Override
+    protected AnyTO getAnyTO(final long key) {
+        return groupDataBinder.getGroupTO(key);
+    }
+
+    @Override
+    protected AnyPatch newPatch(final long key) {
+        GroupPatch patch = new GroupPatch();
+        patch.setKey(key);
+        return patch;
+    }
+
+    @Override
+    protected WorkflowResult<Long> update(final AnyPatch patch) {
+        return gwfAdapter.update((GroupPatch) patch);
+    }
+
+    @Override
+    protected AnyTO doCreate(final AnyTO anyTO, final SyncDelta delta, final ProvisioningReport result) {
+        GroupTO groupTO = GroupTO.class.cast(anyTO);
+
+        Map.Entry<Long, List<PropagationStatus>> created = groupProvisioningManager.create(
+                groupTO,
+                groupOwnerMap,
+                Collections.singleton(profile.getTask().getResource().getKey()),
+                true);
+
+        result.setKey(created.getKey());
+        result.setName(getName(anyTO));
+
+        return getAnyTO(created.getKey());
+    }
+
+    @Override
+    protected AnyTO doUpdate(
+            final AnyTO before,
+            final AnyPatch anyPatch,
+            final SyncDelta delta,
+            final ProvisioningReport result) {
+
+        GroupPatch groupPatch = GroupPatch.class.cast(anyPatch);
+
+        Map.Entry<Long, List<PropagationStatus>> updated = groupProvisioningManager.update(groupPatch, true);
+
+        String groupOwner = null;
+        for (AttrPatch attrPatch : groupPatch.getPlainAttrs()) {
+            if (attrPatch.getOperation() == PatchOperation.ADD_REPLACE && attrPatch.getAttrTO() != null
+                    && attrPatch.getAttrTO().getSchema().isEmpty() && !attrPatch.getAttrTO().getValues().isEmpty()) {
+
+                groupOwner = attrPatch.getAttrTO().getValues().get(0);
+            }
+        }
+        if (groupOwner != null) {
+            groupOwnerMap.put(updated.getKey(), groupOwner);
+        }
+
+        GroupTO after = groupDataBinder.getGroupTO(updated.getKey());
+
+        result.setName(getName(after));
+
+        return after;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPushResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPushResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPushResultHandlerImpl.java
new file mode 100644
index 0000000..c572177
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPushResultHandlerImpl.java
@@ -0,0 +1,70 @@
+/*
+ * 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 org.apache.syncope.common.lib.patch.GroupPatch;
+import org.apache.syncope.common.lib.patch.AnyPatch;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.provisioning.api.WorkflowResult;
+import org.apache.syncope.core.provisioning.api.pushpull.GroupPushResultHandler;
+
+public class GroupPushResultHandlerImpl extends AbstractPushResultHandler implements GroupPushResultHandler {
+
+    @Override
+    protected AnyUtils getAnyUtils() {
+        return anyUtilsFactory.getInstance(AnyTypeKind.GROUP);
+    }
+
+    @Override
+    protected String getName(final Any<?> any) {
+        return Group.class.cast(any).getName();
+    }
+
+    @Override
+    protected Any<?> getAny(final long key) {
+        try {
+            return groupDAO.authFind(key);
+        } catch (Exception e) {
+            LOG.warn("Error retrieving group {}", key, e);
+            return null;
+        }
+    }
+
+    @Override
+    protected AnyTO getAnyTO(final long key) {
+        return groupDataBinder.getGroupTO(key);
+    }
+
+    @Override
+    protected AnyPatch newPatch(final long key) {
+        GroupPatch patch = new GroupPatch();
+        patch.setKey(key);
+        return patch;
+    }
+
+    @Override
+    protected WorkflowResult<Long> update(final AnyPatch patch) {
+        return gwfAdapter.update((GroupPatch) patch);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActions.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActions.java
new file mode 100644
index 0000000..7f7971c
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActions.java
@@ -0,0 +1,333 @@
+/*
+ * 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 java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.patch.AnyPatch;
+import org.apache.syncope.common.lib.patch.MembershipPatch;
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditElements.Result;
+import org.apache.syncope.common.lib.types.ConnConfProperty;
+import org.apache.syncope.common.lib.types.PatchOperation;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
+import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
+import org.apache.syncope.core.provisioning.api.Connector;
+import org.apache.syncope.core.provisioning.api.WorkflowResult;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+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.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.user.UMembership;
+import org.apache.syncope.core.provisioning.api.AuditManager;
+import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
+import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.OperationOptionsBuilder;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.apache.syncope.core.persistence.api.entity.task.PullTask;
+
+/**
+ * Simple action for pulling LDAP groups memberships to Syncope group memberships, when the same resource is
+ * configured for both users and groups.
+ *
+ * @see org.apache.syncope.core.provisioning.java.propagation.LDAPMembershipPropagationActions
+ */
+public class LDAPMembershipPullActions extends DefaultPullActions {
+
+    protected static final Logger LOG = LoggerFactory.getLogger(LDAPMembershipPullActions.class);
+
+    @Autowired
+    protected AnyTypeDAO anyTypeDAO;
+
+    @Autowired
+    protected UserDAO userDAO;
+
+    @Autowired
+    protected GroupDAO groupDAO;
+
+    @Autowired
+    protected UserWorkflowAdapter uwfAdapter;
+
+    @Autowired
+    protected PropagationManager propagationManager;
+
+    @Autowired
+    private PropagationTaskExecutor taskExecutor;
+
+    @Autowired
+    private NotificationManager notificationManager;
+
+    @Autowired
+    private AuditManager auditManager;
+
+    @Autowired
+    private PullUtils pullUtils;
+
+    protected Map<Long, Long> membersBeforeGroupUpdate = Collections.<Long, Long>emptyMap();
+
+    /**
+     * Allows easy subclassing for the ConnId AD connector bundle.
+     *
+     * @param connector A Connector instance to query for the groupMemberAttribute property name
+     * @return the name of the attribute used to keep track of group memberships
+     */
+    protected String getGroupMembershipAttrName(final Connector connector) {
+        ConnConfProperty groupMembership = IterableUtils.find(connector.getConnInstance().getConf(),
+                new Predicate<ConnConfProperty>() {
+
+            @Override
+            public boolean evaluate(final ConnConfProperty property) {
+                return "groupMemberAttribute".equals(property.getSchema().getName())
+                        && property.getValues() != null && !property.getValues().isEmpty();
+            }
+        });
+
+        return groupMembership == null
+                ? "uniquemember"
+                : (String) groupMembership.getValues().get(0);
+    }
+
+    /**
+     * Keep track of members of the group being updated <b>before</b> actual update takes place. This is not needed on
+     * <ul> <li>beforeProvision() - because the pulling group does not exist yet on Syncope</li>
+     * <li>beforeDelete() - because group delete cascades as membership removal for all users involved</li> </ul>
+     *
+     * {@inheritDoc}
+     */
+    @Override
+    public <A extends AnyTO, M extends AnyPatch> SyncDelta beforeUpdate(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta, final A any, final M anyPatch) throws JobExecutionException {
+
+        if (any instanceof GroupTO) {
+            // search for all users assigned to given group
+            Group group = groupDAO.find(any.getKey());
+            if (group != null) {
+                List<UMembership> membs = groupDAO.findUMemberships(group);
+                // save memberships before group update takes place
+                membersBeforeGroupUpdate = new HashMap<>(membs.size());
+                for (UMembership memb : membs) {
+                    membersBeforeGroupUpdate.put(memb.getLeftEnd().getKey(), memb.getKey());
+                }
+            }
+        }
+
+        return super.beforeUpdate(profile, delta, any, anyPatch);
+    }
+
+    /**
+     * Build UserPatch for adding membership to given user, for given group.
+     *
+     * @param userKey user to be assigned membership to given group
+     * @param groupTO group for adding membership
+     * @return UserPatch for user update
+     */
+    protected UserPatch getUserPatch(final Long userKey, final GroupTO groupTO) {
+        UserPatch userPatch = new UserPatch();
+        // no actual modification takes place when user has already the group assigned
+        if (membersBeforeGroupUpdate.containsKey(userKey)) {
+            membersBeforeGroupUpdate.remove(userKey);
+        } else {
+            userPatch.setKey(userKey);
+
+            userPatch.getMemberships().add(
+                    new MembershipPatch.Builder().
+                    operation(PatchOperation.ADD_REPLACE).
+                    membershipTO(new MembershipTO.Builder().group(groupTO.getKey(), null).build()).
+                    build());
+        }
+
+        return userPatch;
+    }
+
+    /**
+     * Read values of attribute returned by getGroupMembershipAttrName(); if not present in the given delta, perform an
+     * additional read on the underlying connector.
+     *
+     * @param delta representing the pulling group
+     * @param connector associated to the current resource
+     * @return value of attribute returned by
+     * {@link #getGroupMembershipAttrName}
+     */
+    protected List<Object> getMembAttrValues(final SyncDelta delta, final Connector connector) {
+        List<Object> result = Collections.<Object>emptyList();
+        String groupMemberName = getGroupMembershipAttrName(connector);
+
+        // first, try to read the configured attribute from delta, returned by the ongoing pull
+        Attribute membAttr = delta.getObject().getAttributeByName(groupMemberName);
+        // if not found, perform an additional read on the underlying connector for the same connector object
+        if (membAttr == null) {
+            OperationOptionsBuilder oob = new OperationOptionsBuilder();
+            oob.setAttributesToGet(groupMemberName);
+            ConnectorObject remoteObj = connector.getObject(ObjectClass.GROUP, delta.getUid(), oob.build());
+            if (remoteObj == null) {
+                LOG.debug("Object for '{}' not found", delta.getUid().getUidValue());
+            } else {
+                membAttr = remoteObj.getAttributeByName(groupMemberName);
+            }
+        }
+        if (membAttr != null && membAttr.getValue() != null) {
+            result = membAttr.getValue();
+        }
+
+        return result;
+    }
+
+    /**
+     * Perform actual modifications (i.e. membership add / remove) for the given group on the given resource.
+     *
+     * @param userPatch modifications to perform on the user
+     * @param resourceName resource to be propagated for changes
+     */
+    protected void userUpdate(final UserPatch userPatch, final String resourceName) {
+        if (userPatch.getKey() == 0) {
+            return;
+        }
+
+        Result result;
+
+        WorkflowResult<Pair<UserPatch, Boolean>> updated = null;
+
+        try {
+            updated = uwfAdapter.update(userPatch);
+
+            List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(
+                    updated, false, Collections.singleton(resourceName));
+
+            taskExecutor.execute(tasks);
+            result = Result.SUCCESS;
+        } catch (PropagationException e) {
+            result = Result.FAILURE;
+            LOG.error("Could not propagate {}", userPatch, e);
+        } catch (Exception e) {
+            result = Result.FAILURE;
+            LOG.error("Could not perform update {}", userPatch, e);
+        }
+
+        notificationManager.createTasks(AuditElements.EventCategoryType.PULL,
+                this.getClass().getSimpleName(),
+                null,
+                "update",
+                result,
+                null, // searching for before object is too much expensive ... 
+                updated == null ? null : updated.getResult().getKey(),
+                userPatch,
+                resourceName);
+
+        auditManager.audit(AuditElements.EventCategoryType.PULL,
+                this.getClass().getSimpleName(),
+                null,
+                "update",
+                result,
+                null, // searching for before object is too much expensive ... 
+                updated == null ? null : updated.getResult().getKey(),
+                userPatch,
+                resourceName);
+    }
+
+    /**
+     * Pull Syncope memberships with the situation read on the external resource's group.
+     *
+     * @param profile pull profile
+     * @param delta representing the pullong group
+     * @param groupTO group after modification performed by the handler
+     * @throws JobExecutionException if anything goes wrong
+     */
+    protected void pullMemberships(
+            final ProvisioningProfile<?, ?> profile, final SyncDelta delta, final GroupTO groupTO)
+            throws JobExecutionException {
+
+        ProvisioningTask task = profile.getTask();
+        ExternalResource resource = task.getResource();
+        Connector connector = profile.getConnector();
+
+        for (Object membValue : getMembAttrValues(delta, connector)) {
+            Long userKey = pullUtils.findMatchingAnyKey(
+                    anyTypeDAO.findUser(),
+                    membValue.toString(),
+                    profile.getTask().getResource(),
+                    profile.getConnector());
+            if (userKey != null) {
+                UserPatch userPatch = getUserPatch(userKey, groupTO);
+                userUpdate(userPatch, resource.getKey());
+            }
+        }
+
+        // finally remove any residual membership that was present before group update but not any more
+        for (Map.Entry<Long, Long> member : membersBeforeGroupUpdate.entrySet()) {
+            UserPatch userPatch = new UserPatch();
+            userPatch.setKey(member.getKey());
+
+            userPatch.getMemberships().add(
+                    new MembershipPatch.Builder().
+                    operation(PatchOperation.DELETE).
+                    membershipTO(new MembershipTO.Builder().group(groupTO.getKey(), null).build()).
+                    build());
+
+            userUpdate(userPatch, resource.getKey());
+        }
+    }
+
+    /**
+     * Pull membership at group pull time (because PullJob first pulls users then groups).
+     * {@inheritDoc}
+     */
+    @Override
+    public <A extends AnyTO> void after(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final A any,
+            final ProvisioningReport result) throws JobExecutionException {
+
+        if (!(profile.getTask() instanceof PullTask)) {
+            return;
+        }
+
+        if (!(any instanceof GroupTO)
+                || profile.getTask().getResource().getProvision(anyTypeDAO.findUser()) == null
+                || profile.getTask().getResource().getProvision(anyTypeDAO.findUser()).getMapping() == null) {
+
+            super.after(profile, delta, any, result);
+        } else {
+            pullMemberships(profile, delta, (GroupTO) any);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActions.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActions.java
new file mode 100644
index 0000000..2d61396
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActions.java
@@ -0,0 +1,124 @@
+/*
+ * 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 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.AnyTO;
+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.api.pushpull.ProvisioningReport;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.crypto.codec.Base64;
+import org.springframework.security.crypto.codec.Hex;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * A {@link org.apache.syncope.core.provisioning.api.pushpull.PullActions} implementation which allows the ability to 
+ * import passwords from an LDAP backend that are hashed.
+ */
+public class LDAPPasswordPullActions extends DefaultPullActions {
+
+    protected static final Logger LOG = LoggerFactory.getLogger(LDAPPasswordPullActions.class);
+
+    @Autowired
+    private UserDAO userDAO;
+
+    private String encodedPassword;
+
+    private CipherAlgorithm cipher;
+
+    @Transactional(readOnly = true)
+    @Override
+    public <A extends AnyTO> SyncDelta beforeProvision(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final A any) throws JobExecutionException {
+
+        if (any instanceof UserTO) {
+            String password = ((UserTO) any).getPassword();
+            parseEncodedPassword(password);
+        }
+
+        return delta;
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public <A extends AnyTO, M extends AnyPatch> SyncDelta beforeUpdate(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final A any,
+            final M anyPatch) throws JobExecutionException {
+
+        if (anyPatch instanceof UserPatch) {
+            PasswordPatch modPassword = ((UserPatch) anyPatch).getPassword();
+            parseEncodedPassword(modPassword == null ? null : modPassword.getValue());
+        }
+
+        return delta;
+    }
+
+    private void parseEncodedPassword(final String password) {
+        if (password != null && password.startsWith("{")) {
+            int closingBracketIndex = password.indexOf('}');
+            String digest = password.substring(1, password.indexOf('}'));
+            if (digest != null) {
+                digest = digest.toUpperCase();
+            }
+            try {
+                encodedPassword = password.substring(closingBracketIndex + 1);
+                cipher = CipherAlgorithm.valueOf(digest);
+            } catch (IllegalArgumentException e) {
+                LOG.error("Cipher algorithm not allowed: {}", digest, e);
+                encodedPassword = null;
+            }
+        }
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public <A extends AnyTO> void after(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final A any,
+            final ProvisioningReport result) throws JobExecutionException {
+
+        if (any instanceof UserTO && encodedPassword != null && cipher != null) {
+            User syncopeUser = userDAO.find(any.getKey());
+            if (syncopeUser != null) {
+                byte[] encodedPasswordBytes = Base64.decode(encodedPassword.getBytes());
+                char[] encodedHex = Hex.encode(encodedPasswordBytes);
+                String encodedHexStr = new String(encodedHex).toUpperCase();
+
+                syncopeUser.setEncodedPassword(encodedHexStr, cipher);
+            }
+            encodedPassword = null;
+            cipher = null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PlainAttrsPullCorrelationRule.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PlainAttrsPullCorrelationRule.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PlainAttrsPullCorrelationRule.java
new file mode 100644
index 0000000..d752cd5
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PlainAttrsPullCorrelationRule.java
@@ -0,0 +1,115 @@
+/*
+ * 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 java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.syncope.core.provisioning.java.MappingManagerImpl;
+import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
+import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+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.provisioning.api.data.MappingItemTransformer;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.apache.syncope.core.provisioning.api.pushpull.PullCorrelationRule;
+
+public class PlainAttrsPullCorrelationRule implements PullCorrelationRule {
+
+    private final List<String> plainSchemaNames;
+
+    private final Provision provision;
+
+    public PlainAttrsPullCorrelationRule(final String[] plainSchemaNames, final Provision provision) {
+        this.plainSchemaNames = Arrays.asList(plainSchemaNames);
+        this.provision = provision;
+    }
+
+    @Override
+    public SearchCond getSearchCond(final ConnectorObject connObj) {
+        Map<String, MappingItem> mappingItems = new HashMap<>();
+        for (MappingItem item : MappingManagerImpl.getPullMappingItems(provision)) {
+            mappingItems.put(item.getIntAttrName(), item);
+        }
+
+        // search for anys by attribute(s) specified in the policy
+        SearchCond searchCond = null;
+
+        for (String schema : plainSchemaNames) {
+            Attribute attr = mappingItems.get(schema) == null
+                    ? null
+                    : connObj.getAttributeByName(mappingItems.get(schema).getExtAttrName());
+            if (attr == null) {
+                throw new IllegalArgumentException(
+                        "Connector object does not contains the attributes to perform the search: " + schema);
+            }
+
+            List<Object> values = attr.getValue();
+            for (MappingItemTransformer transformer
+                    : MappingManagerImpl.getMappingItemTransformers(mappingItems.get(schema))) {
+
+                values = transformer.beforePull(values);
+            }
+
+            AttributeCond.Type type;
+            String expression = null;
+
+            if (values == null || values.isEmpty() || (values.size() == 1 && values.get(0) == null)) {
+                type = AttributeCond.Type.ISNULL;
+            } else {
+                type = AttributeCond.Type.EQ;
+                expression = values.size() > 1
+                        ? values.toString()
+                        : values.get(0).toString();
+            }
+
+            SearchCond nodeCond;
+            // users: just key or username can be selected
+            // groups: just key or name can be selected
+            // any objects: just key can be selected
+            if ("key".equalsIgnoreCase(schema)
+                    || "username".equalsIgnoreCase(schema) || "name".equalsIgnoreCase(schema)) {
+
+                AnyCond cond = new AnyCond();
+                cond.setSchema(schema);
+                cond.setType(type);
+                cond.setExpression(expression);
+
+                nodeCond = SearchCond.getLeafCond(cond);
+            } else {
+                AttributeCond cond = new AttributeCond();
+                cond.setSchema(schema);
+                cond.setType(type);
+                cond.setExpression(expression);
+
+                nodeCond = SearchCond.getLeafCond(cond);
+            }
+
+            searchCond = searchCond == null
+                    ? nodeCond
+                    : SearchCond.getAndCond(searchCond, nodeCond);
+        }
+
+        return searchCond;
+    }
+
+}