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;
+ }
+
+}