http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java new file mode 100644 index 0000000..ed1a702 --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java @@ -0,0 +1,248 @@ +/* + * 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.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.commons.collections4.IteratorUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.policy.PullPolicySpec; +import org.apache.syncope.core.provisioning.java.MappingManagerImpl; +import org.apache.syncope.core.spring.ApplicationContextProvider; +import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; +import org.apache.syncope.core.persistence.api.entity.VirSchema; +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.persistence.api.entity.task.ProvisioningTask; +import org.apache.syncope.core.provisioning.api.Connector; +import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile; +import org.identityconnectors.framework.common.objects.SyncResultsHandler; +import org.identityconnectors.framework.common.objects.SyncToken; +import org.quartz.JobExecutionException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.apache.syncope.core.provisioning.api.pushpull.ReconciliationFilterBuilder; +import org.apache.syncope.core.persistence.api.entity.task.PullTask; +import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPullResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.PullActions; +import org.apache.syncope.core.provisioning.api.pushpull.GroupPullResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.UserPullResultHandler; + +public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> { + + @Autowired + private UserDAO userDAO; + + @Autowired + private GroupDAO groupDAO; + + @Autowired + private VirSchemaDAO virSchemaDAO; + + @Autowired + protected PullUtils pullUtils; + + protected void setGroupOwners(final GroupPullResultHandler ghandler) { + for (Map.Entry<Long, String> entry : ghandler.getGroupOwnerMap().entrySet()) { + Group group = groupDAO.find(entry.getKey()); + if (group == null) { + throw new NotFoundException("Group " + entry.getKey()); + } + + if (StringUtils.isBlank(entry.getValue())) { + group.setGroupOwner(null); + group.setUserOwner(null); + } else { + Long userKey = pullUtils.findMatchingAnyKey( + anyTypeDAO.findUser(), + entry.getValue(), + ghandler.getProfile().getTask().getResource(), + ghandler.getProfile().getConnector()); + + if (userKey == null) { + Long groupKey = pullUtils.findMatchingAnyKey( + anyTypeDAO.findGroup(), + entry.getValue(), + ghandler.getProfile().getTask().getResource(), + ghandler.getProfile().getConnector()); + + if (groupKey != null) { + group.setGroupOwner(groupDAO.find(groupKey)); + } + } else { + group.setUserOwner(userDAO.find(userKey)); + } + } + + groupDAO.save(group); + } + } + + @Override + protected String doExecuteProvisioning( + final PullTask pullTask, + final Connector connector, + final boolean dryRun) throws JobExecutionException { + + LOG.debug("Executing pull on {}", pullTask.getResource()); + + List<PullActions> actions = new ArrayList<>(); + for (String className : pullTask.getActionsClassNames()) { + try { + Class<?> actionsClass = Class.forName(className); + PullActions pullActions = (PullActions) ApplicationContextProvider.getBeanFactory(). + createBean(actionsClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true); + + actions.add(pullActions); + } catch (Exception e) { + LOG.warn("Class '{}' not found", className, e); + } + } + + ProvisioningProfile<PullTask, PullActions> profile = new ProvisioningProfile<>(connector, pullTask); + profile.getActions().addAll(actions); + profile.setDryRun(dryRun); + profile.setResAct(getPullPolicySpec(pullTask).getConflictResolutionAction()); + + // Prepare handler for SyncDelta objects (any objects) + AnyObjectPullResultHandler ahandler = (AnyObjectPullResultHandler) ApplicationContextProvider.getBeanFactory(). + createBean(AnyObjectPullResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); + ahandler.setProfile(profile); + + // Prepare handler for SyncDelta objects (users) + UserPullResultHandler uhandler = (UserPullResultHandler) ApplicationContextProvider.getBeanFactory(). + createBean(UserPullResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); + uhandler.setProfile(profile); + + // Prepare handler for SyncDelta objects (groups) + GroupPullResultHandler ghandler = (GroupPullResultHandler) ApplicationContextProvider.getBeanFactory(). + createBean(GroupPullResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); + ghandler.setProfile(profile); + + if (!profile.isDryRun()) { + for (PullActions action : actions) { + action.beforeAll(profile); + } + } + + for (Provision provision : pullTask.getResource().getProvisions()) { + if (provision.getMapping() != null) { + SyncResultsHandler handler; + switch (provision.getAnyType().getKind()) { + case USER: + handler = uhandler; + break; + + case GROUP: + handler = ghandler; + break; + + case ANY_OBJECT: + default: + handler = ahandler; + } + + try { + Set<MappingItem> linkinMappingItems = new HashSet<>(); + for (VirSchema virSchema : virSchemaDAO.findByProvision(provision)) { + linkinMappingItems.add(virSchema.asLinkingMappingItem()); + } + Iterator<MappingItem> mapItems = IteratorUtils.chainedIterator( + provision.getMapping().getItems().iterator(), + linkinMappingItems.iterator()); + + switch (pullTask.getPullMode()) { + case INCREMENTAL: + SyncToken latestSyncToken = connector.getLatestSyncToken(provision.getObjectClass()); + connector.sync(provision.getObjectClass(), + provision.getSyncToken(), + handler, + MappingManagerImpl.buildOperationOptions(mapItems)); + if (!dryRun) { + provision.setSyncToken(latestSyncToken); + resourceDAO.save(provision.getResource()); + } + break; + + case FILTERED_RECONCILIATION: + ReconciliationFilterBuilder filterBuilder = + (ReconciliationFilterBuilder) ApplicationContextProvider.getBeanFactory(). + createBean(Class.forName(pullTask.getReconciliationFilterBuilderClassName()), + AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); + connector.filteredReconciliation(provision.getObjectClass(), + filterBuilder, + handler, + MappingManagerImpl.buildOperationOptions(mapItems)); + break; + + case FULL_RECONCILIATION: + default: + connector.fullReconciliation(provision.getObjectClass(), + handler, + MappingManagerImpl.buildOperationOptions(mapItems)); + break; + } + } catch (Throwable t) { + throw new JobExecutionException("While pulling from connector", t); + } + } + } + + try { + setGroupOwners(ghandler); + } catch (Exception e) { + LOG.error("While setting group owners", e); + } + + if (!profile.isDryRun()) { + for (PullActions action : actions) { + action.afterAll(profile); + } + } + + String result = createReport(profile.getResults(), pullTask.getResource().getPullTraceLevel(), dryRun); + + LOG.debug("Pull result: {}", result); + + return result; + } + + private PullPolicySpec getPullPolicySpec(final ProvisioningTask task) { + PullPolicySpec pullPolicySpec; + + if (task instanceof PullTask) { + pullPolicySpec = task.getResource().getPullPolicy() == null + ? null + : task.getResource().getPullPolicy().getSpecification(); + } else { + pullPolicySpec = null; + } + + // step required because the call <policy>.getSpecification() could return a null value + return pullPolicySpec == null ? new PullPolicySpec() : pullPolicySpec; + } +}
http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullUtils.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullUtils.java new file mode 100644 index 0000000..a0f558c --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullUtils.java @@ -0,0 +1,317 @@ +/* + * 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.Collections; +import java.util.List; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.policy.PullPolicySpec; +import org.apache.syncope.core.provisioning.java.MappingManagerImpl; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException; +import org.apache.syncope.core.persistence.api.dao.AnyDAO; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +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.Any; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.AnyUtils; +import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.resource.MappingItem; +import org.apache.syncope.core.persistence.api.entity.resource.Provision; +import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask; +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.data.MappingItemTransformer; +import org.identityconnectors.framework.common.objects.Attribute; +import org.identityconnectors.framework.common.objects.AttributeUtil; +import org.identityconnectors.framework.common.objects.ConnectorObject; +import org.identityconnectors.framework.common.objects.Name; +import org.identityconnectors.framework.common.objects.OperationalAttributes; +import org.identityconnectors.framework.common.objects.ResultsHandler; +import org.identityconnectors.framework.common.objects.filter.EqualsFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import org.apache.syncope.core.provisioning.api.pushpull.PullCorrelationRule; + +@Transactional(readOnly = true) +@Component +public class PullUtils { + + private static final Logger LOG = LoggerFactory.getLogger(PullUtils.class); + + /** + * Schema DAO. + */ + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + /** + * Any Object DAO. + */ + @Autowired + private AnyObjectDAO anyObjectDAO; + + /** + * User DAO. + */ + @Autowired + private UserDAO userDAO; + + /** + * Group DAO. + */ + @Autowired + private GroupDAO groupDAO; + + /** + * Search DAO. + */ + @Autowired + private AnySearchDAO searchDAO; + + @Autowired + private AnyUtilsFactory anyUtilsFactory; + + public Long findMatchingAnyKey( + final AnyType anyType, + final String name, + final ExternalResource resource, + final Connector connector) { + + Provision provision = resource.getProvision(anyType); + if (provision == null) { + return null; + } + + Long result = null; + + AnyUtils anyUtils = anyUtilsFactory.getInstance(anyType.getKind()); + + final List<ConnectorObject> found = new ArrayList<>(); + connector.search(provision.getObjectClass(), + new EqualsFilter(new Name(name)), + new ResultsHandler() { + + @Override + public boolean handle(final ConnectorObject obj) { + return found.add(obj); + } + }, + MappingManagerImpl.buildOperationOptions(MappingManagerImpl.getPullMappingItems(provision).iterator())); + + if (found.isEmpty()) { + LOG.debug("No {} found on {} with __NAME__ {}", provision.getObjectClass(), resource, name); + } else { + if (found.size() > 1) { + LOG.warn("More than one {} found on {} with __NAME__ {} - taking first only", + provision.getObjectClass(), resource, name); + } + + ConnectorObject connObj = found.iterator().next(); + try { + List<Long> anyKeys = findExisting(connObj.getUid().getUidValue(), connObj, provision, anyUtils); + if (anyKeys.isEmpty()) { + LOG.debug("No matching {} found for {}, aborting", anyUtils.getAnyTypeKind(), connObj); + } else { + if (anyKeys.size() > 1) { + LOG.warn("More than one {} found {} - taking first only", anyUtils.getAnyTypeKind(), anyKeys); + } + + result = anyKeys.iterator().next(); + } + } catch (IllegalArgumentException e) { + LOG.warn(e.getMessage()); + } + } + + return result; + } + + private AnyDAO<?> getAnyDAO(final MappingItem connObjectKeyItem) { + return AnyTypeKind.USER == connObjectKeyItem.getIntMappingType().getAnyTypeKind() + ? userDAO + : AnyTypeKind.ANY_OBJECT == connObjectKeyItem.getIntMappingType().getAnyTypeKind() + ? anyObjectDAO + : groupDAO; + } + + private List<Long> findByConnObjectKeyItem( + final String uid, final Provision provision, final AnyUtils anyUtils) { + + List<Long> result = new ArrayList<>(); + + MappingItem connObjectKeyItem = MappingManagerImpl.getConnObjectKeyItem(provision); + + String transfUid = uid; + for (MappingItemTransformer transformer : MappingManagerImpl.getMappingItemTransformers(connObjectKeyItem)) { + List<Object> output = transformer.beforePull(Collections.<Object>singletonList(transfUid)); + if (output != null && !output.isEmpty()) { + transfUid = output.get(0).toString(); + } + } + + switch (connObjectKeyItem.getIntMappingType()) { + case UserPlainSchema: + case GroupPlainSchema: + case AnyObjectPlainSchema: + PlainAttrValue value = anyUtils.newPlainAttrValue(); + + PlainSchema schema = plainSchemaDAO.find(connObjectKeyItem.getIntAttrName()); + if (schema == null) { + value.setStringValue(transfUid); + } else { + try { + value.parseValue(schema, transfUid); + } catch (ParsingValidationException e) { + LOG.error("While parsing provided __UID__ {}", transfUid, e); + value.setStringValue(transfUid); + } + } + + List<? extends Any<?>> anys = + getAnyDAO(connObjectKeyItem).findByAttrValue(connObjectKeyItem.getIntAttrName(), value); + for (Any<?> any : anys) { + result.add(any.getKey()); + } + break; + + case UserDerivedSchema: + case GroupDerivedSchema: + case AnyObjectDerivedSchema: + anys = getAnyDAO(connObjectKeyItem).findByDerAttrValue(connObjectKeyItem.getIntAttrName(), transfUid); + for (Any<?> any : anys) { + result.add(any.getKey()); + } + break; + + case UserKey: + case GroupKey: + case AnyObjectKey: + Any<?> any = getAnyDAO(connObjectKeyItem).find(Long.parseLong(transfUid)); + if (any != null) { + result.add(any.getKey()); + } + break; + + case Username: + User user = userDAO.find(transfUid); + if (user != null) { + result.add(user.getKey()); + } + break; + + case GroupName: + Group group = groupDAO.find(transfUid); + if (group != null) { + result.add(group.getKey()); + } + break; + + default: + LOG.error("Invalid connObjectKey type '{}'", connObjectKeyItem.getIntMappingType()); + } + + return result; + } + + private List<Long> findByCorrelationRule( + final ConnectorObject connObj, final PullCorrelationRule rule, final AnyTypeKind type) { + + List<Long> result = new ArrayList<>(); + for (Any<?> any : searchDAO.search(rule.getSearchCond(connObj), type)) { + result.add(any.getKey()); + } + + return result; + } + + private PullCorrelationRule getCorrelationRule(final Provision provision, final PullPolicySpec policySpec) { + PullCorrelationRule result = null; + + String pullCorrelationRule = policySpec.getCorrelationRules().get(provision.getAnyType().getKey()); + if (StringUtils.isNotBlank(pullCorrelationRule)) { + if (pullCorrelationRule.charAt(0) == '[') { + result = new PlainAttrsPullCorrelationRule( + POJOHelper.deserialize(pullCorrelationRule, String[].class), provision); + } else { + try { + result = (PullCorrelationRule) Class.forName(pullCorrelationRule).newInstance(); + } catch (Exception e) { + LOG.error("Failure instantiating correlation rule class '{}'", pullCorrelationRule, e); + } + } + } + + return result; + } + + /** + * Find any objects based on mapped uid value (or previous uid value, if updated). + * + * @param uid for finding by connObjectKey + * @param connObj for finding by attribute value + * @param provision external resource + * @param anyUtils any util + * @return list of matching users / groups + */ + public List<Long> findExisting( + final String uid, + final ConnectorObject connObj, + final Provision provision, + final AnyUtils anyUtils) { + + PullPolicySpec pullPolicySpec = null; + if (provision.getResource().getPullPolicy() != null) { + pullPolicySpec = provision.getResource().getPullPolicy().getSpecification(); + } + + PullCorrelationRule pullRule = null; + if (pullPolicySpec != null) { + pullRule = getCorrelationRule(provision, pullPolicySpec); + } + + return pullRule == null + ? findByConnObjectKeyItem(uid, provision, anyUtils) + : findByCorrelationRule(connObj, pullRule, anyUtils.getAnyTypeKind()); + } + + public Boolean readEnabled(final ConnectorObject connectorObject, final ProvisioningTask task) { + Boolean enabled = null; + if (task.isPullStatus()) { + Attribute status = AttributeUtil.find(OperationalAttributes.ENABLE_NAME, connectorObject.getAttributes()); + if (status != null && status.getValue() != null && !status.getValue().isEmpty()) { + enabled = (Boolean) status.getValue().get(0); + } + } + + return enabled; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java new file mode 100644 index 0000000..563c64a --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java @@ -0,0 +1,206 @@ +/* + * 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.Collections; +import java.util.List; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.search.SearchCondConverter; +import org.apache.syncope.core.spring.ApplicationContextProvider; +import org.apache.syncope.core.persistence.api.dao.AnyDAO; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +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.dao.search.OrderByClause; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.resource.Provision; +import org.apache.syncope.core.persistence.api.entity.task.PushTask; +import org.apache.syncope.core.provisioning.api.Connector; +import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPushResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.GroupPushResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile; +import org.apache.syncope.core.provisioning.api.pushpull.PushActions; +import org.apache.syncope.core.provisioning.api.pushpull.SyncopePushResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.UserPushResultHandler; +import org.quartz.JobExecutionException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.support.AbstractBeanDefinition; + +public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> { + + private static final int PAGE_SIZE = 1000; + + /** + * User DAO. + */ + @Autowired + private UserDAO userDAO; + + /** + * Search DAO. + */ + @Autowired + private AnySearchDAO searchDAO; + + /** + * Group DAO. + */ + @Autowired + private GroupDAO groupDAO; + + @Autowired + private AnyObjectDAO anyObjectDAO; + + private AnyDAO<?> getAnyDAO(final AnyTypeKind anyTypeKind) { + AnyDAO<?> result; + switch (anyTypeKind) { + case USER: + result = userDAO; + break; + + case GROUP: + result = groupDAO; + break; + + case ANY_OBJECT: + default: + result = anyObjectDAO; + } + + return result; + } + + protected void handle( + final List<? extends Any<?>> anys, + final SyncopePushResultHandler handler, + final ExternalResource resource) + throws JobExecutionException { + + for (Any<?> any : anys) { + try { + handler.handle(any.getKey()); + } catch (Exception e) { + LOG.warn("Failure pushing '{}' on '{}'", any, resource, e); + throw new JobExecutionException("While pushing " + any + " on " + resource, e); + } + } + } + + @Override + protected String doExecuteProvisioning( + final PushTask pushTask, + final Connector connector, + final boolean dryRun) throws JobExecutionException { + + LOG.debug("Executing push on {}", pushTask.getResource()); + + List<PushActions> actions = new ArrayList<>(); + for (String className : pushTask.getActionsClassNames()) { + try { + Class<?> actionsClass = Class.forName(className); + + PushActions pushActions = (PushActions) ApplicationContextProvider.getBeanFactory(). + createBean(actionsClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true); + actions.add(pushActions); + } catch (Exception e) { + LOG.info("Class '{}' not found", className, e); + } + } + + ProvisioningProfile<PushTask, PushActions> profile = new ProvisioningProfile<>(connector, pushTask); + profile.setDryRun(dryRun); + profile.setResAct(null); + + AnyObjectPushResultHandler ahandler = + (AnyObjectPushResultHandler) ApplicationContextProvider.getBeanFactory(). + createBean(AnyObjectPushResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); + ahandler.setProfile(profile); + + UserPushResultHandler uhandler = + (UserPushResultHandler) ApplicationContextProvider.getBeanFactory(). + createBean(UserPushResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); + uhandler.setProfile(profile); + + GroupPushResultHandler ghandler = + (GroupPushResultHandler) ApplicationContextProvider.getBeanFactory(). + createBean(GroupPushResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); + ghandler.setProfile(profile); + + if (!profile.isDryRun()) { + for (PushActions action : actions) { + action.beforeAll(profile); + } + } + + for (Provision provision : pushTask.getResource().getProvisions()) { + if (provision.getMapping() != null) { + AnyDAO<?> anyDAO = getAnyDAO(provision.getAnyType().getKind()); + + SyncopePushResultHandler handler; + switch (provision.getAnyType().getKind()) { + case USER: + handler = uhandler; + break; + + case GROUP: + handler = ghandler; + break; + + case ANY_OBJECT: + default: + handler = ahandler; + } + + String filter = pushTask.getFilter(provision.getAnyType()) == null + ? null + : pushTask.getFilter(provision.getAnyType()).getFIQLCond(); + if (StringUtils.isBlank(filter)) { + handle(anyDAO.findAll(), handler, pushTask.getResource()); + } else { + int count = anyDAO.count(SyncopeConstants.FULL_ADMIN_REALMS); + for (int page = 1; page <= (count / PAGE_SIZE) + 1; page++) { + List<? extends Any<?>> anys = searchDAO.search( + SyncopeConstants.FULL_ADMIN_REALMS, + SearchCondConverter.convert(filter), + page, + PAGE_SIZE, + Collections.<OrderByClause>emptyList(), + provision.getAnyType().getKind()); + handle(anys, handler, pushTask.getResource()); + } + } + } + } + + if (!profile.isDryRun()) { + for (PushActions action : actions) { + action.afterAll(profile); + } + } + + String result = createReport(profile.getResults(), pushTask.getResource().getPullTraceLevel(), dryRun); + LOG.debug("Sync result: {}", result); + return result; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPullResultHandlerImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPullResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPullResultHandlerImpl.java new file mode 100644 index 0000000..ddc01fb --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPullResultHandlerImpl.java @@ -0,0 +1,133 @@ +/* + * 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.tuple.Pair; +import org.apache.syncope.common.lib.patch.AnyPatch; +import org.apache.syncope.common.lib.patch.UserPatch; +import org.apache.syncope.common.lib.to.AnyTO; +import org.apache.syncope.common.lib.to.PropagationStatus; +import org.apache.syncope.common.lib.to.UserTO; +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.UserPullResultHandler; + +public class UserPullResultHandlerImpl extends AbstractPullResultHandler implements UserPullResultHandler { + + @Override + protected AnyUtils getAnyUtils() { + return anyUtilsFactory.getInstance(AnyTypeKind.USER); + } + + @Override + protected String getName(final AnyTO anyTO) { + return UserTO.class.cast(anyTO).getUsername(); + } + + @Override + protected ProvisioningManager<?, ?> getProvisioningManager() { + return userProvisioningManager; + } + + @Override + protected Any<?> getAny(final long key) { + try { + return userDAO.authFind(key); + } catch (Exception e) { + LOG.warn("Error retrieving user {}", key, e); + return null; + } + } + + @Override + protected AnyTO getAnyTO(final long key) { + return userDataBinder.getUserTO(key); + } + + @Override + protected AnyPatch newPatch(final long key) { + UserPatch patch = new UserPatch(); + patch.setKey(key); + return patch; + } + + @Override + protected WorkflowResult<Long> update(final AnyPatch patch) { + WorkflowResult<Pair<UserPatch, Boolean>> update = uwfAdapter.update((UserPatch) patch); + return new WorkflowResult<>( + update.getResult().getLeft().getKey(), update.getPropByRes(), update.getPerformedTasks()); + } + + @Override + protected AnyTO doCreate(final AnyTO anyTO, final SyncDelta delta, final ProvisioningReport result) { + UserTO userTO = UserTO.class.cast(anyTO); + + Boolean enabled = pullUtils.readEnabled(delta.getObject(), profile.getTask()); + Map.Entry<Long, List<PropagationStatus>> created = + userProvisioningManager.create(userTO, true, true, enabled, + 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) { + + UserPatch userPatch = UserPatch.class.cast(anyPatch); + Boolean enabled = pullUtils.readEnabled(delta.getObject(), profile.getTask()); + + Map.Entry<Long, List<PropagationStatus>> updated = userProvisioningManager.update( + userPatch, + result, + enabled, + Collections.singleton(profile.getTask().getResource().getKey()), + true); + + return getAnyTO(updated.getKey()); + } + + @Override + protected void doDelete(final AnyTypeKind kind, final Long key) { + try { + userProvisioningManager.delete( + key, Collections.<String>singleton(profile.getTask().getResource().getKey()), true); + } catch (Exception e) { + // A propagation failure doesn't imply a pull failure. + // The propagation exception status will be reported into the propagation task execution. + LOG.error("Could not propagate user " + key, e); + } + + uwfAdapter.delete(key); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPushResultHandlerImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPushResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPushResultHandlerImpl.java new file mode 100644 index 0000000..bc63845 --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPushResultHandlerImpl.java @@ -0,0 +1,96 @@ +/* + * 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.List; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.patch.AnyPatch; +import org.apache.syncope.common.lib.patch.UserPatch; +import org.apache.syncope.common.lib.to.AnyTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.PropagationByResource; +import org.apache.syncope.common.lib.types.ResourceOperation; +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.user.User; +import org.apache.syncope.core.provisioning.api.WorkflowResult; +import org.apache.syncope.core.provisioning.api.pushpull.UserPushResultHandler; + +public class UserPushResultHandlerImpl extends AbstractPushResultHandler implements UserPushResultHandler { + + @Override + protected AnyUtils getAnyUtils() { + return anyUtilsFactory.getInstance(AnyTypeKind.USER); + } + + @Override + 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.getUserCreateTasks( + before.getKey(), + null, + enabled, + propByRes, + before.getVirAttrs(), + noPropResources)); + } + + @Override + protected String getName(final Any<?> any) { + return User.class.cast(any).getUsername(); + } + + @Override + protected Any<?> getAny(final long key) { + try { + return userDAO.authFind(key); + } catch (Exception e) { + LOG.warn("Error retrieving user {}", key, e); + return null; + } + } + + @Override + protected AnyTO getAnyTO(final long key) { + return userDataBinder.getUserTO(key); + } + + @Override + protected AnyPatch newPatch(final long key) { + UserPatch patch = new UserPatch(); + patch.setKey(key); + return patch; + } + + @Override + protected WorkflowResult<Long> update(final AnyPatch patch) { + WorkflowResult<Pair<UserPatch, Boolean>> update = uwfAdapter.update((UserPatch) patch); + return new WorkflowResult<>( + update.getResult().getLeft().getKey(), update.getPropByRes(), update.getPerformedTasks()); + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractProvisioningJobDelegate.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractProvisioningJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractProvisioningJobDelegate.java deleted file mode 100644 index bd13365..0000000 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractProvisioningJobDelegate.java +++ /dev/null @@ -1,434 +0,0 @@ -/* - * 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.syncpull; - -import java.lang.reflect.ParameterizedType; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import javax.annotation.Resource; -import org.apache.syncope.common.lib.types.TraceLevel; -import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; -import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; -import org.apache.syncope.core.persistence.api.dao.PolicyDAO; -import org.apache.syncope.core.persistence.api.entity.AnyType; -import org.apache.syncope.core.persistence.api.entity.resource.Mapping; -import org.apache.syncope.core.persistence.api.entity.resource.Provision; -import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask; -import org.apache.syncope.core.persistence.api.entity.task.TaskExec; -import org.apache.syncope.core.provisioning.api.Connector; -import org.apache.syncope.core.provisioning.api.ConnectorFactory; -import org.apache.syncope.core.provisioning.api.syncpull.ProvisioningReport; -import org.apache.syncope.core.provisioning.java.job.AbstractSchedTaskJobDelegate; -import org.apache.syncope.core.provisioning.java.job.TaskJob; -import org.quartz.JobExecutionException; -import org.springframework.beans.factory.annotation.Autowired; - -public abstract class AbstractProvisioningJobDelegate<T extends ProvisioningTask> - extends AbstractSchedTaskJobDelegate { - - @Resource(name = "adminUser") - protected String adminUser; - - /** - * ConnInstance loader. - */ - @Autowired - protected ConnectorFactory connFactory; - - @Autowired - protected AnyTypeDAO anyTypeDAO; - - /** - * Resource DAO. - */ - @Autowired - protected ExternalResourceDAO resourceDAO; - - /** - * Policy DAO. - */ - @Autowired - protected PolicyDAO policyDAO; - - /** - * Create a textual report of the synchronization, based on the trace level. - * - * @param provResults Sync results - * @param syncTraceLevel Sync trace level - * @param dryRun dry run? - * @return report as string - */ - protected String createReport(final Collection<ProvisioningReport> provResults, final TraceLevel syncTraceLevel, - final boolean dryRun) { - - if (syncTraceLevel == TraceLevel.NONE) { - return null; - } - - StringBuilder report = new StringBuilder(); - - if (dryRun) { - report.append("==>Dry run only, no modifications were made<==\n\n"); - } - - List<ProvisioningReport> uSuccCreate = new ArrayList<>(); - List<ProvisioningReport> uFailCreate = new ArrayList<>(); - List<ProvisioningReport> uSuccUpdate = new ArrayList<>(); - List<ProvisioningReport> uFailUpdate = new ArrayList<>(); - List<ProvisioningReport> uSuccDelete = new ArrayList<>(); - List<ProvisioningReport> uFailDelete = new ArrayList<>(); - List<ProvisioningReport> uSuccNone = new ArrayList<>(); - List<ProvisioningReport> uIgnore = new ArrayList<>(); - List<ProvisioningReport> gSuccCreate = new ArrayList<>(); - List<ProvisioningReport> gFailCreate = new ArrayList<>(); - List<ProvisioningReport> gSuccUpdate = new ArrayList<>(); - List<ProvisioningReport> gFailUpdate = new ArrayList<>(); - List<ProvisioningReport> gSuccDelete = new ArrayList<>(); - List<ProvisioningReport> gFailDelete = new ArrayList<>(); - List<ProvisioningReport> gSuccNone = new ArrayList<>(); - List<ProvisioningReport> gIgnore = new ArrayList<>(); - List<ProvisioningReport> aSuccCreate = new ArrayList<>(); - List<ProvisioningReport> aFailCreate = new ArrayList<>(); - List<ProvisioningReport> aSuccUpdate = new ArrayList<>(); - List<ProvisioningReport> aFailUpdate = new ArrayList<>(); - List<ProvisioningReport> aSuccDelete = new ArrayList<>(); - List<ProvisioningReport> aFailDelete = new ArrayList<>(); - List<ProvisioningReport> aSuccNone = new ArrayList<>(); - List<ProvisioningReport> aIgnore = new ArrayList<>(); - - for (ProvisioningReport provResult : provResults) { - AnyType anyType = anyTypeDAO.find(provResult.getAnyType()); - - switch (provResult.getStatus()) { - case SUCCESS: - switch (provResult.getOperation()) { - case CREATE: - switch (anyType.getKind()) { - case USER: - uSuccCreate.add(provResult); - break; - - case GROUP: - gSuccCreate.add(provResult); - break; - - case ANY_OBJECT: - default: - aSuccCreate.add(provResult); - } - break; - - case UPDATE: - switch (anyType.getKind()) { - case USER: - uSuccUpdate.add(provResult); - break; - - case GROUP: - gSuccUpdate.add(provResult); - break; - - case ANY_OBJECT: - default: - aSuccUpdate.add(provResult); - } - break; - - case DELETE: - switch (anyType.getKind()) { - case USER: - uSuccDelete.add(provResult); - break; - - case GROUP: - gSuccDelete.add(provResult); - break; - - case ANY_OBJECT: - default: - aSuccDelete.add(provResult); - } - break; - - case NONE: - switch (anyType.getKind()) { - case USER: - uSuccNone.add(provResult); - break; - - case GROUP: - gSuccNone.add(provResult); - break; - - case ANY_OBJECT: - default: - aSuccNone.add(provResult); - } - break; - - default: - } - break; - - case FAILURE: - switch (provResult.getOperation()) { - case CREATE: - switch (anyType.getKind()) { - case USER: - uFailCreate.add(provResult); - break; - - case GROUP: - gFailCreate.add(provResult); - break; - - case ANY_OBJECT: - default: - aFailCreate.add(provResult); - } - break; - - case UPDATE: - switch (anyType.getKind()) { - case USER: - uFailUpdate.add(provResult); - break; - - case GROUP: - gFailUpdate.add(provResult); - break; - - case ANY_OBJECT: - default: - aFailUpdate.add(provResult); - } - break; - - case DELETE: - switch (anyType.getKind()) { - case USER: - uFailDelete.add(provResult); - break; - - case GROUP: - gFailDelete.add(provResult); - break; - - case ANY_OBJECT: - default: - aFailDelete.add(provResult); - } - break; - - default: - } - break; - - case IGNORE: - switch (anyType.getKind()) { - case USER: - uIgnore.add(provResult); - break; - - case GROUP: - gIgnore.add(provResult); - break; - - case ANY_OBJECT: - default: - aIgnore.add(provResult); - } - break; - - default: - } - } - - // Summary, also to be included for FAILURE and ALL, so create it anyway. - report.append("Users "). - append("[created/failures]: ").append(uSuccCreate.size()).append('/').append(uFailCreate.size()). - append(' '). - append("[updated/failures]: ").append(uSuccUpdate.size()).append('/').append(uFailUpdate.size()). - append(' '). - append("[deleted/failures]: ").append(uSuccDelete.size()).append('/').append(uFailDelete.size()). - append(' '). - append("[no operation/ignored]: ").append(uSuccNone.size()).append('/').append(uIgnore.size()). - append('\n'); - report.append("Groups "). - append("[created/failures]: ").append(gSuccCreate.size()).append('/').append(gFailCreate.size()). - append(' '). - append("[updated/failures]: ").append(gSuccUpdate.size()).append('/').append(gFailUpdate.size()). - append(' '). - append("[deleted/failures]: ").append(gSuccDelete.size()).append('/').append(gFailDelete.size()). - append(' '). - append("[no operation/ignored]: ").append(gSuccNone.size()).append('/').append(gIgnore.size()). - append('\n'); - report.append("Any objects "). - append("[created/failures]: ").append(aSuccCreate.size()).append('/').append(aFailCreate.size()). - append(' '). - append("[updated/failures]: ").append(aSuccUpdate.size()).append('/').append(aFailUpdate.size()). - append(' '). - append("[deleted/failures]: ").append(aSuccDelete.size()).append('/').append(aFailDelete.size()). - append(' '). - append("[no operation/ignored]: ").append(aSuccNone.size()).append('/').append(aIgnore.size()); - - // Failures - if (syncTraceLevel == TraceLevel.FAILURES || syncTraceLevel == TraceLevel.ALL) { - if (!uFailCreate.isEmpty()) { - report.append("\n\nUsers failed to create: "); - report.append(ProvisioningReport.produceReport(uFailCreate, syncTraceLevel)); - } - if (!uFailUpdate.isEmpty()) { - report.append("\nUsers failed to update: "); - report.append(ProvisioningReport.produceReport(uFailUpdate, syncTraceLevel)); - } - if (!uFailDelete.isEmpty()) { - report.append("\nUsers failed to delete: "); - report.append(ProvisioningReport.produceReport(uFailDelete, syncTraceLevel)); - } - - if (!gFailCreate.isEmpty()) { - report.append("\n\nGroups failed to create: "); - report.append(ProvisioningReport.produceReport(gFailCreate, syncTraceLevel)); - } - if (!gFailUpdate.isEmpty()) { - report.append("\nGroups failed to update: "); - report.append(ProvisioningReport.produceReport(gFailUpdate, syncTraceLevel)); - } - if (!gFailDelete.isEmpty()) { - report.append("\nGroups failed to delete: "); - report.append(ProvisioningReport.produceReport(gFailDelete, syncTraceLevel)); - } - - if (!aFailCreate.isEmpty()) { - report.append("\nAny objects failed to create: "); - report.append(ProvisioningReport.produceReport(aFailCreate, syncTraceLevel)); - } - if (!aFailUpdate.isEmpty()) { - report.append("\nAny objects failed to update: "); - report.append(ProvisioningReport.produceReport(aFailUpdate, syncTraceLevel)); - } - if (!aFailDelete.isEmpty()) { - report.append("\nAny objects failed to delete: "); - report.append(ProvisioningReport.produceReport(aFailDelete, syncTraceLevel)); - } - } - - // Succeeded, only if on 'ALL' level - if (syncTraceLevel == TraceLevel.ALL) { - report.append("\n\nUsers created:\n"). - append(ProvisioningReport.produceReport(uSuccCreate, syncTraceLevel)). - append("\nUsers updated:\n"). - append(ProvisioningReport.produceReport(uSuccUpdate, syncTraceLevel)). - append("\nUsers deleted:\n"). - append(ProvisioningReport.produceReport(uSuccDelete, syncTraceLevel)). - append("\nUsers no operation:\n"). - append(ProvisioningReport.produceReport(uSuccNone, syncTraceLevel)). - append("\nUsers ignored:\n"). - append(ProvisioningReport.produceReport(uIgnore, syncTraceLevel)); - report.append("\n\nGroups created:\n"). - append(ProvisioningReport.produceReport(gSuccCreate, syncTraceLevel)). - append("\nGroups updated:\n"). - append(ProvisioningReport.produceReport(gSuccUpdate, syncTraceLevel)). - append("\nGroups deleted:\n"). - append(ProvisioningReport.produceReport(gSuccDelete, syncTraceLevel)). - append("\nGroups no operation:\n"). - append(ProvisioningReport.produceReport(gSuccNone, syncTraceLevel)). - append("\nGroups ignored:\n"). - append(ProvisioningReport.produceReport(gSuccNone, syncTraceLevel)); - report.append("\n\nAny objects created:\n"). - append(ProvisioningReport.produceReport(aSuccCreate, syncTraceLevel)). - append("\nAny objects updated:\n"). - append(ProvisioningReport.produceReport(aSuccUpdate, syncTraceLevel)). - append("\nAny objects deleted:\n"). - append(ProvisioningReport.produceReport(aSuccDelete, syncTraceLevel)). - append("\nAny objects no operation:\n"). - append(ProvisioningReport.produceReport(aSuccNone, syncTraceLevel)). - append("\nAny objects ignored:\n"). - append(ProvisioningReport.produceReport(aSuccNone, syncTraceLevel)); - } - - return report.toString(); - } - - @Override - protected String doExecute(final boolean dryRun) throws JobExecutionException { - try { - Class<T> clazz = getTaskClassReference(); - if (!clazz.isAssignableFrom(task.getClass())) { - throw new JobExecutionException("Task " + task.getKey() + " isn't a ProvisioningTask"); - } - - T provisioningTask = clazz.cast(task); - - Connector connector; - try { - connector = connFactory.getConnector(provisioningTask.getResource()); - } catch (Exception e) { - String msg = String.format("Connector instance bean for resource %s and connInstance %s not found", - provisioningTask.getResource(), provisioningTask.getResource().getConnector()); - throw new JobExecutionException(msg, e); - } - - boolean noMapping = true; - for (Provision provision : provisioningTask.getResource().getProvisions()) { - Mapping mapping = provision.getMapping(); - if (mapping != null) { - noMapping = false; - if (mapping.getConnObjectKeyItem() == null) { - throw new JobExecutionException( - "Invalid ConnObjectKey mapping for provision " + provision); - } - } - } - if (noMapping) { - return "No mapping configured for both users and groups: aborting..."; - } - - return doExecuteProvisioning( - provisioningTask, - connector, - dryRun); - } catch (Throwable t) { - LOG.error("While executing provisioning job {}", getClass().getName(), t); - throw t; - } - } - - protected abstract String doExecuteProvisioning( - final T task, - final Connector connector, - final boolean dryRun) throws JobExecutionException; - - @Override - protected boolean hasToBeRegistered(final TaskExec execution) { - final ProvisioningTask provTask = (ProvisioningTask) task; - - // True if either failed and failures have to be registered, or if ALL has to be registered. - return (TaskJob.Status.valueOf(execution.getStatus()) == TaskJob.Status.FAILURE - && provTask.getResource().getSyncTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) - || provTask.getResource().getSyncTraceLevel().ordinal() >= TraceLevel.SUMMARY.ordinal(); - } - - @SuppressWarnings("unchecked") - private Class<T> getTaskClassReference() { - return (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; - } -} http://git-wip-us.apache.org/repos/asf/syncope/blob/61a7fdd3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractPushResultHandler.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractPushResultHandler.java deleted file mode 100644 index 258e106..0000000 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/syncpull/AbstractPushResultHandler.java +++ /dev/null @@ -1,434 +0,0 @@ -/* - * 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.syncpull; - -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.syncpull.ProvisioningReport; -import org.apache.syncope.core.provisioning.api.syncpull.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.syncpull.IgnoreProvisionException; -import org.apache.syncope.core.provisioning.api.syncpull.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().isSyncStatus() - ? ((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().isSyncStatus() ? 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()); - } -}
