http://git-wip-us.apache.org/repos/asf/syncope/blob/22a9e12e/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandler.java ---------------------------------------------------------------------- diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandler.java index 14c00c7,0000000..58202b7 mode 100644,000000..100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandler.java @@@ -1,312 -1,0 +1,312 @@@ +/* + * 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; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.mod.AttrMod; +import org.apache.syncope.common.lib.to.AttrTO; +import org.apache.syncope.common.lib.types.AttributableType; +import org.apache.syncope.common.lib.types.IntMappingType; +import org.apache.syncope.common.lib.types.MappingPurpose; +import org.apache.syncope.common.lib.types.PropagationByResource; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.core.persistence.api.dao.MembershipDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.dao.VirAttrDAO; +import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; +import org.apache.syncope.core.persistence.api.entity.Attributable; +import org.apache.syncope.core.persistence.api.entity.AttributableUtils; +import org.apache.syncope.core.persistence.api.entity.AttributableUtilsFactory; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.MappingItem; +import org.apache.syncope.core.persistence.api.entity.Subject; +import org.apache.syncope.core.persistence.api.entity.VirAttr; +import org.apache.syncope.core.persistence.api.entity.VirSchema; +import org.apache.syncope.core.persistence.api.entity.membership.MVirAttr; +import org.apache.syncope.core.persistence.api.entity.membership.MVirAttrTemplate; +import org.apache.syncope.core.persistence.api.entity.membership.Membership; +import org.apache.syncope.core.persistence.api.entity.group.GVirAttr; +import org.apache.syncope.core.persistence.api.entity.group.GVirAttrTemplate; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.user.UVirAttr; +import org.apache.syncope.core.persistence.api.entity.user.UVirSchema; +import org.apache.syncope.core.persistence.api.entity.user.User; +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; + +@Component +@Transactional(rollbackFor = { Throwable.class }) +public class VirAttrHandler { + + private static final Logger LOG = LoggerFactory.getLogger(VirAttrHandler.class); + + @Autowired + private VirSchemaDAO virSchemaDAO; + + @Autowired + private VirAttrDAO virAttrDAO; + + @Autowired + private UserDAO userDAO; + + @Autowired + private MembershipDAO membershipDAO; + + @Autowired + private AttributableUtilsFactory attrUtilsFactory; + + public <T extends VirSchema> T getVirSchema(final String virSchemaName, final Class<T> reference) { + T virtualSchema = null; + if (StringUtils.isNotBlank(virSchemaName)) { + virtualSchema = virSchemaDAO.find(virSchemaName, reference); + + if (virtualSchema == null) { + LOG.debug("Ignoring invalid virtual schema {}", virSchemaName); + } + } + + return virtualSchema; + } + + public void setVirAttrSchema(final Attributable<?, ?, ?> attributable, + final VirAttr virAttr, final VirSchema virSchema) { + + if (virAttr instanceof UVirAttr) { + ((UVirAttr) virAttr).setSchema((UVirSchema) virSchema); + } else if (virAttr instanceof GVirAttr) { + GVirAttrTemplate template = ((Group) attributable). + getAttrTemplate(GVirAttrTemplate.class, virSchema.getKey()); + if (template != null) { + ((GVirAttr) virAttr).setTemplate(template); + } + } else if (virAttr instanceof MVirAttr) { + MVirAttrTemplate template = + ((Membership) attributable).getGroup(). + getAttrTemplate(MVirAttrTemplate.class, virSchema.getKey()); + if (template != null) { + ((MVirAttr) virAttr).setTemplate(template); + } + } + } + + public void updateOnResourcesIfMappingMatches(final AttributableUtils attrUtils, final String schemaKey, + final Set<ExternalResource> resources, final IntMappingType mappingType, + final PropagationByResource propByRes) { + + for (ExternalResource resource : resources) { + for (MappingItem mapItem : attrUtils.getMappingItems(resource, MappingPurpose.PROPAGATION)) { + if (schemaKey.equals(mapItem.getIntAttrName()) && mapItem.getIntMappingType() == mappingType) { + propByRes.add(ResourceOperation.UPDATE, resource.getKey()); + } + } + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public PropagationByResource fillVirtual(final Attributable attributable, + final Set<String> vAttrsToBeRemoved, final Set<AttrMod> vAttrsToBeUpdated, + final AttributableUtils attrUtils) { + + PropagationByResource propByRes = new PropagationByResource(); + + Set<ExternalResource> externalResources = new HashSet<>(); + if (attributable instanceof User) { + externalResources.addAll(userDAO.findAllResources((User) attributable)); + } else if (attributable instanceof Group) { + externalResources.addAll(((Group) attributable).getResources()); + } else if (attributable instanceof Membership) { + externalResources.addAll(userDAO.findAllResources(((Membership) attributable).getUser())); + externalResources.addAll(((Membership) attributable).getGroup().getResources()); + } + + // 1. virtual attributes to be removed + for (String vAttrToBeRemoved : vAttrsToBeRemoved) { + VirSchema virSchema = getVirSchema(vAttrToBeRemoved, attrUtils.virSchemaClass()); + if (virSchema != null) { + VirAttr virAttr = attributable.getVirAttr(virSchema.getKey()); + if (virAttr == null) { + LOG.debug("No virtual attribute found for schema {}", virSchema.getKey()); + } else { + attributable.removeVirAttr(virAttr); + virAttrDAO.delete(virAttr); + } + + for (ExternalResource resource : externalResources) { + for (MappingItem mapItem : attrUtils.getMappingItems(resource, MappingPurpose.PROPAGATION)) { + if (virSchema.getKey().equals(mapItem.getIntAttrName()) + && mapItem.getIntMappingType() == attrUtils.virIntMappingType()) { + + propByRes.add(ResourceOperation.UPDATE, resource.getKey()); + + // Using virtual attribute as AccountId must be avoided + if (mapItem.isAccountid() && virAttr != null && !virAttr.getValues().isEmpty()) { + propByRes.addOldAccountId(resource.getKey(), virAttr.getValues().get(0)); + } + } + } + } + } + } + + LOG.debug("Virtual attributes to be removed:\n{}", propByRes); + + // 2. virtual attributes to be updated + for (AttrMod vAttrToBeUpdated : vAttrsToBeUpdated) { + VirSchema virSchema = getVirSchema(vAttrToBeUpdated.getSchema(), attrUtils.virSchemaClass()); + VirAttr virAttr = null; + if (virSchema != null) { + virAttr = attributable.getVirAttr(virSchema.getKey()); + if (virAttr == null) { + virAttr = attrUtils.newVirAttr(); + setVirAttrSchema(attributable, virAttr, virSchema); + if (virAttr.getSchema() == null) { + LOG.debug("Ignoring {} because no valid schema or template was found", vAttrToBeUpdated); + } else { + attributable.addVirAttr(virAttr); + } + } + } + + if (virSchema != null && virAttr != null && virAttr.getSchema() != null) { + if (attributable instanceof Subject) { + updateOnResourcesIfMappingMatches(attrUtils, virSchema.getKey(), + externalResources, attrUtils.derIntMappingType(), propByRes); + } else if (attributable instanceof Membership) { + updateOnResourcesIfMappingMatches(attrUtils, virSchema.getKey(), + externalResources, IntMappingType.MembershipVirtualSchema, propByRes); + } + + final List<String> values = new ArrayList<>(virAttr.getValues()); + values.removeAll(vAttrToBeUpdated.getValuesToBeRemoved()); + values.addAll(vAttrToBeUpdated.getValuesToBeAdded()); + + virAttr.getValues().clear(); + virAttr.getValues().addAll(values); + + // Owner cannot be specified before otherwise a virtual attribute remove will be invalidated. + virAttr.setOwner(attributable); + } + } + + LOG.debug("Virtual attributes to be added:\n{}", propByRes); + + return propByRes; + } + + /** + * Add virtual attributes and specify values to be propagated. + * + * @param attributable attributable. + * @param vAttrs virtual attributes to be added. + * @param attrUtils attributable util. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void fillVirtual(final Attributable attributable, final Collection<AttrTO> vAttrs, + final AttributableUtils attrUtils) { + + for (AttrTO attributeTO : vAttrs) { + VirAttr virAttr = attributable.getVirAttr(attributeTO.getSchema()); + if (virAttr == null) { + VirSchema virSchema = getVirSchema(attributeTO.getSchema(), attrUtils.virSchemaClass()); + if (virSchema != null) { + virAttr = attrUtils.newVirAttr(); + setVirAttrSchema(attributable, virAttr, virSchema); + if (virAttr.getSchema() == null) { + LOG.debug("Ignoring {} because no valid schema or template was found", attributeTO); + } else { + virAttr.setOwner(attributable); + attributable.addVirAttr(virAttr); + virAttr.getValues().clear(); + virAttr.getValues().addAll(attributeTO.getValues()); + } + } + } else { + virAttr.getValues().clear(); + virAttr.getValues().addAll(attributeTO.getValues()); + } + } + } + + /** + * SYNCOPE-459: build virtual attribute changes in case no other changes were made. + * + * @param key user id + * @param vAttrsToBeRemoved virtual attributes to be removed. + * @param vAttrsToBeUpdated virtual attributes to be updated. + * @return operations to be performed on external resources for virtual attributes changes + */ + public PropagationByResource fillVirtual( + final Long key, final Set<String> vAttrsToBeRemoved, final Set<AttrMod> vAttrsToBeUpdated) { + + return fillVirtual( + userDAO.authFetch(key), + vAttrsToBeRemoved, + vAttrsToBeUpdated, + attrUtilsFactory.getInstance(AttributableType.USER)); + } + + private Set<String> getAttrNames(final List<? extends VirAttr> virAttrs) { + final Set<String> virAttrNames = new HashSet<>(); + for (VirAttr attr : virAttrs) { + virAttrNames.add(attr.getSchema().getKey()); + } + return virAttrNames; + } + + /** + * SYNCOPE-501: build membership virtual attribute changes in case no other changes were made. + * + * @param key user key + * @param groupKey group key + * @param membershipKey membership key + * @param vAttrsToBeRemoved virtual attributes to be removed. + * @param vAttrsToBeUpdated virtual attributes to be updated. + * @param isRemoval flag to check if fill is on removed or added membership + * @return operations to be performed on external resources for membership virtual attributes changes + */ + public PropagationByResource fillMembershipVirtual( + final Long key, final Long groupKey, final Long membershipKey, final Set<String> vAttrsToBeRemoved, + final Set<AttrMod> vAttrsToBeUpdated, final boolean isRemoval) { + - final Membership membership = membershipKey == null ++ Membership membership = membershipKey == null + ? userDAO.authFetch(key).getMembership(groupKey) + : membershipDAO.authFetch(membershipKey); + + return membership == null ? new PropagationByResource() : isRemoval + ? fillVirtual( + membership, + getAttrNames(membership.getVirAttrs()), + vAttrsToBeUpdated, + attrUtilsFactory.getInstance(AttributableType.MEMBERSHIP)) + : fillVirtual( + membership, + vAttrsToBeRemoved, + vAttrsToBeUpdated, + attrUtilsFactory.getInstance(AttributableType.MEMBERSHIP)); + } + +}
http://git-wip-us.apache.org/repos/asf/syncope/blob/22a9e12e/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java ---------------------------------------------------------------------- diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java index 2939095,0000000..d21afc5 mode 100644,000000..100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java @@@ -1,550 -1,0 +1,549 @@@ +/* + * 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.propagation; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.Predicate; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.mod.AttrMod; +import org.apache.syncope.common.lib.mod.MembershipMod; +import org.apache.syncope.common.lib.mod.UserMod; +import org.apache.syncope.common.lib.to.AttrTO; +import org.apache.syncope.common.lib.to.MembershipTO; +import org.apache.syncope.common.lib.types.AttributableType; +import org.apache.syncope.common.lib.types.MappingPurpose; +import org.apache.syncope.common.lib.types.PropagationByResource; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +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.AttributableUtils; +import org.apache.syncope.core.persistence.api.entity.AttributableUtilsFactory; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.MappingItem; +import org.apache.syncope.core.persistence.api.entity.Subject; +import org.apache.syncope.core.persistence.api.entity.VirAttr; +import org.apache.syncope.core.persistence.api.entity.membership.Membership; +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.user.User; +import org.apache.syncope.core.provisioning.api.WorkflowResult; +import org.apache.syncope.core.provisioning.api.propagation.PropagationManager; +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; +import org.apache.syncope.core.provisioning.java.VirAttrHandler; +import org.apache.syncope.core.misc.ConnObjectUtils; +import org.apache.syncope.core.misc.MappingUtils; +import org.apache.syncope.core.misc.jexl.JexlUtils; +import org.identityconnectors.framework.common.objects.Attribute; +import org.identityconnectors.framework.common.objects.AttributeBuilder; +import org.identityconnectors.framework.common.objects.AttributeUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +/** + * Manage the data propagation to external resources. + */ +@Transactional(rollbackFor = { Throwable.class }) +public class PropagationManagerImpl implements PropagationManager { + + /** + * Logger. + */ + protected static final Logger LOG = LoggerFactory.getLogger(PropagationManager.class); + + /** + * User DAO. + */ + @Autowired + protected UserDAO userDAO; + + /** + * Group DAO. + */ + @Autowired + protected GroupDAO groupDAO; + + /** + * Resource DAO. + */ + @Autowired + protected ExternalResourceDAO resourceDAO; + + @Autowired + protected EntityFactory entityFactory; + + /** + * ConnObjectUtils. + */ + @Autowired + protected ConnObjectUtils connObjectUtils; + + @Autowired + protected AttributableUtilsFactory attrUtilsFactory; + + @Autowired + protected VirAttrHandler virAttrHandler; + + @Override + public List<PropagationTask> getUserCreateTasks( + final Long key, + final Boolean enable, + final PropagationByResource propByRes, + final String password, + final Collection<AttrTO> vAttrs, + final Collection<MembershipTO> membershipTOs, + final Collection<String> noPropResourceNames) { + + User user = userDAO.authFetch(key); + if (vAttrs != null && !vAttrs.isEmpty()) { + virAttrHandler.fillVirtual(user, vAttrs, attrUtilsFactory.getInstance(AttributableType.USER)); + } + for (final Membership membership : user.getMemberships()) { + if (membership.getVirAttrs() != null && !membership.getVirAttrs().isEmpty()) { + MembershipTO membershipTO = CollectionUtils.find(membershipTOs, new Predicate<MembershipTO>() { + + @Override + public boolean evaluate(final MembershipTO membershipTO) { + return membershipTO.getGroupKey() == membership.getGroup().getKey(); + } + }); + if (membershipTO != null) { + virAttrHandler.fillVirtual(membership, + membershipTO.getVirAttrs(), attrUtilsFactory.getInstance(AttributableType.MEMBERSHIP)); + } + } + } + return getCreateTaskIds(user, password, enable, propByRes, noPropResourceNames); + } + + /** + * Create the group on every associated resource. + * + * @param wfResult group to be propagated (and info associated), as per result from workflow + * @param vAttrs virtual attributes to be set + * @param noPropResourceNames external resources performing not to be considered for propagation + * @return list of propagation tasks + */ + @Override + public List<PropagationTask> getGroupCreateTasks( + final WorkflowResult<Long> wfResult, + final Collection<AttrTO> vAttrs, + final Collection<String> noPropResourceNames) { + + return getGroupCreateTasks(wfResult.getResult(), vAttrs, wfResult.getPropByRes(), noPropResourceNames); + } + + /** + * Create the group on every associated resource. + * + * @param key group key + * @param vAttrs virtual attributes to be set + * @param propByRes operation to be performed per resource + * @param noPropResourceNames external resources performing not to be considered for propagation + * @return list of propagation tasks + */ + @Override + public List<PropagationTask> getGroupCreateTasks( + final Long key, + final Collection<AttrTO> vAttrs, + final PropagationByResource propByRes, + final Collection<String> noPropResourceNames) { + + Group group = groupDAO.authFetch(key); + if (vAttrs != null && !vAttrs.isEmpty()) { + virAttrHandler.fillVirtual(group, vAttrs, attrUtilsFactory.getInstance(AttributableType.GROUP)); + } + + return getCreateTaskIds(group, null, null, propByRes, noPropResourceNames); + } + + protected List<PropagationTask> getCreateTaskIds(final Subject<?, ?, ?> subject, + final String password, final Boolean enable, + final PropagationByResource propByRes, + final Collection<String> noPropResourceNames) { + + if (propByRes == null || propByRes.isEmpty()) { + return Collections.<PropagationTask>emptyList(); + } + + if (noPropResourceNames != null) { + propByRes.get(ResourceOperation.CREATE).removeAll(noPropResourceNames); + } + + return createTasks(subject, password, true, null, null, null, null, enable, false, propByRes); + } + + /** + * Performs update on each resource associated to the user excluding the specified into 'resourceNames' parameter. + * + * @param user to be propagated + * @param enable whether user must be enabled or not + * @param noPropResourceNames external resource names not to be considered for propagation + * @return list of propagation tasks + */ + @Override + public List<PropagationTask> getUserUpdateTasks(final User user, final Boolean enable, + final Collection<String> noPropResourceNames) { + + return getUpdateTasks( + user, // user to be updated on external resources + null, // no password + false, + enable, // status to be propagated + Collections.<String>emptySet(), // no virtual attributes to be managed + Collections.<AttrMod>emptySet(), // no virtual attributes to be managed + null, // no propagation by resources + noPropResourceNames, + Collections.<MembershipMod>emptySet()); + } + + /** + * Performs update on each resource associated to the user. + * + * @param wfResult user to be propagated (and info associated), as per result from workflow + * @param changePwd whether password should be included for propagation attributes or not + * @param noPropResourceNames external resources not to be considered for propagation + * @return list of propagation tasks + */ + @Override + public List<PropagationTask> getUserUpdateTasks(final WorkflowResult<Pair<UserMod, Boolean>> wfResult, + final boolean changePwd, final Collection<String> noPropResourceNames) { + + User user = userDAO.authFetch(wfResult.getResult().getKey().getKey()); + return getUpdateTasks(user, + wfResult.getResult().getKey().getPassword(), + changePwd, + wfResult.getResult().getValue(), + wfResult.getResult().getKey().getVirAttrsToRemove(), + wfResult.getResult().getKey().getVirAttrsToUpdate(), + wfResult.getPropByRes(), + noPropResourceNames, + wfResult.getResult().getKey().getMembershipsToAdd()); + } + + @Override + public List<PropagationTask> getUserUpdateTasks(final WorkflowResult<Pair<UserMod, Boolean>> wfResult) { + UserMod userMod = wfResult.getResult().getKey(); + + // Propagate password update only to requested resources + List<PropagationTask> tasks = new ArrayList<>(); + if (userMod.getPwdPropRequest() == null) { + // a. no specific password propagation request: generate propagation tasks for any resource associated + tasks = getUserUpdateTasks(wfResult, false, null); + } else { + // b. generate the propagation task list in two phases: first the ones containing password, + // the the rest (with no password) + final PropagationByResource origPropByRes = new PropagationByResource(); + origPropByRes.merge(wfResult.getPropByRes()); + + Set<String> pwdResourceNames = new HashSet<>(userMod.getPwdPropRequest().getResourceNames()); + Collection<String> currentResourceNames = userDAO.findAllResourceNames(userDAO.authFetch(userMod.getKey())); + pwdResourceNames.retainAll(currentResourceNames); + PropagationByResource pwdPropByRes = new PropagationByResource(); + pwdPropByRes.addAll(ResourceOperation.UPDATE, pwdResourceNames); + if (!pwdPropByRes.isEmpty()) { + Set<String> toBeExcluded = new HashSet<>(currentResourceNames); + toBeExcluded.addAll(userMod.getResourcesToAdd()); + toBeExcluded.removeAll(pwdResourceNames); + tasks.addAll(getUserUpdateTasks(wfResult, true, toBeExcluded)); + } + - final PropagationByResource nonPwdPropByRes = new PropagationByResource(); ++ PropagationByResource nonPwdPropByRes = new PropagationByResource(); + nonPwdPropByRes.merge(origPropByRes); + nonPwdPropByRes.removeAll(pwdResourceNames); + nonPwdPropByRes.purge(); + if (!nonPwdPropByRes.isEmpty()) { + tasks.addAll(getUserUpdateTasks(wfResult, false, pwdResourceNames)); + } + } + + return tasks; + } + + @Override + public List<PropagationTask> getGroupUpdateTasks(final WorkflowResult<Long> wfResult, + final Set<String> vAttrsToBeRemoved, final Set<AttrMod> vAttrsToBeUpdated, + final Set<String> noPropResourceNames) { + + Group group = groupDAO.authFetch(wfResult.getResult()); + return getUpdateTasks(group, null, false, null, + vAttrsToBeRemoved, vAttrsToBeUpdated, wfResult.getPropByRes(), noPropResourceNames, + Collections.<MembershipMod>emptySet()); + } + + @Override + public List<PropagationTask> getUpdateTasks(final Subject<?, ?, ?> subject, + final String password, final boolean changePwd, final Boolean enable, + final Set<String> vAttrsToBeRemoved, final Set<AttrMod> vAttrsToBeUpdated, + final PropagationByResource propByRes, final Collection<String> noPropResourceNames, + final Set<MembershipMod> membershipsToAdd) { + + PropagationByResource localPropByRes = virAttrHandler.fillVirtual(subject, vAttrsToBeRemoved == null + ? Collections.<String>emptySet() + : vAttrsToBeRemoved, vAttrsToBeUpdated == null + ? Collections.<AttrMod>emptySet() + : vAttrsToBeUpdated, attrUtilsFactory.getInstance(subject)); + + // SYNCOPE-458 fill membership virtual attributes + Collection<String> resourceNames; + if (subject instanceof User) { + User user = (User) subject; + resourceNames = userDAO.findAllResourceNames(user); + for (final Membership membership : user.getMemberships()) { + if (membership.getVirAttrs() != null && !membership.getVirAttrs().isEmpty()) { - final MembershipMod membershipMod = CollectionUtils.find(membershipsToAdd, - new Predicate<MembershipMod>() { - - @Override - public boolean evaluate(final MembershipMod membershipMod) { - return membershipMod.getGroup() == membership.getGroup().getKey(); - } - }); - if (membershipMod != null) { - virAttrHandler.fillVirtual(membership, membershipMod.getVirAttrsToRemove() == null ++ MembershipMod membMod = CollectionUtils.find(membershipsToAdd, new Predicate<MembershipMod>() { ++ ++ @Override ++ public boolean evaluate(final MembershipMod membershipMod) { ++ return membershipMod.getGroup() == membership.getGroup().getKey(); ++ } ++ }); ++ if (membMod != null) { ++ virAttrHandler.fillVirtual(membership, membMod.getVirAttrsToRemove() == null + ? Collections.<String>emptySet() - : membershipMod.getVirAttrsToRemove(), - membershipMod.getVirAttrsToUpdate() == null ? Collections.<AttrMod>emptySet() - : membershipMod.getVirAttrsToUpdate(), attrUtilsFactory.getInstance( ++ : membMod.getVirAttrsToRemove(), ++ membMod.getVirAttrsToUpdate() == null ? Collections.<AttrMod>emptySet() ++ : membMod.getVirAttrsToUpdate(), attrUtilsFactory.getInstance( + AttributableType.MEMBERSHIP)); + } + } + } + } else { + resourceNames = subject.getResourceNames(); + } + + if (propByRes == null || propByRes.isEmpty()) { + localPropByRes.addAll(ResourceOperation.UPDATE, resourceNames); + } else { + localPropByRes.merge(propByRes); + } + + if (noPropResourceNames != null) { + localPropByRes.removeAll(noPropResourceNames); + } + + Map<String, AttrMod> vAttrsToBeUpdatedMap = null; + if (vAttrsToBeUpdated != null) { + vAttrsToBeUpdatedMap = new HashMap<>(); + for (AttrMod attrMod : vAttrsToBeUpdated) { + vAttrsToBeUpdatedMap.put(attrMod.getSchema(), attrMod); + } + } + + // SYNCOPE-458 fill membership virtual attributes to be updated map + Map<String, AttrMod> membVAttrsToBeUpdatedMap = new HashMap<>(); + for (MembershipMod membershipMod : membershipsToAdd) { + for (AttrMod attrMod : membershipMod.getVirAttrsToUpdate()) { + membVAttrsToBeUpdatedMap.put(attrMod.getSchema(), attrMod); + } + } + + // SYNCOPE-458 fill membership virtual attributes to be removed set + final Set<String> membVAttrsToBeRemoved = new HashSet<>(); + for (MembershipMod membershipMod : membershipsToAdd) { + membVAttrsToBeRemoved.addAll(membershipMod.getVirAttrsToRemove()); + } + + return createTasks(subject, password, changePwd, + vAttrsToBeRemoved, vAttrsToBeUpdatedMap, membVAttrsToBeRemoved, membVAttrsToBeUpdatedMap, enable, false, + localPropByRes); + } + + @Override + public List<PropagationTask> getUserDeleteTasks(final Long userKey, final Collection<String> noPropResourceNames) { + + User user = userDAO.authFetch(userKey); + return getDeleteTaskIds(user, userDAO.findAllResourceNames(user), noPropResourceNames); + } + + @Override + public List<PropagationTask> getUserDeleteTasks( + final Long userKey, final Set<String> resourceNames, final Collection<String> noPropResourceNames) { + + User user = userDAO.authFetch(userKey); + return getDeleteTaskIds(user, resourceNames, noPropResourceNames); + } + + @Override + public List<PropagationTask> getUserDeleteTasks(final WorkflowResult<Long> wfResult) { + User user = userDAO.authFetch(wfResult.getResult()); + return createTasks(user, null, false, null, null, null, null, false, true, wfResult.getPropByRes()); + } + + @Override + public List<PropagationTask> getGroupDeleteTasks(final Long groupKey) { + + return getGroupDeleteTasks(groupKey, Collections.<String>emptySet()); + } + + @Override + public List<PropagationTask> getGroupDeleteTasks(final Long groupKey, final String noPropResourceName) { + + return getGroupDeleteTasks(groupKey, Collections.<String>singleton(noPropResourceName)); + } + + @Override + public List<PropagationTask> getGroupDeleteTasks( + final Long groupKey, final Collection<String> noPropResourceNames) { + + Group group = groupDAO.authFetch(groupKey); + return getDeleteTaskIds(group, group.getResourceNames(), noPropResourceNames); + } + + @Override + public List<PropagationTask> getGroupDeleteTasks( + final Long groupKey, final Set<String> resourceNames, final Collection<String> noPropResourceNames) { + + Group group = groupDAO.authFetch(groupKey); + return getDeleteTaskIds(group, resourceNames, noPropResourceNames); + } + + protected List<PropagationTask> getDeleteTaskIds( + final Subject<?, ?, ?> subject, + final Collection<String> resourceNames, + final Collection<String> noPropResourceNames) { + - final PropagationByResource propByRes = new PropagationByResource(); ++ PropagationByResource propByRes = new PropagationByResource(); + propByRes.set(ResourceOperation.DELETE, resourceNames); + if (noPropResourceNames != null && !noPropResourceNames.isEmpty()) { + propByRes.get(ResourceOperation.DELETE).removeAll(noPropResourceNames); + } + return createTasks(subject, null, false, null, null, null, null, false, true, propByRes); + } + + /** + * Create propagation tasks. + * + * @param subject user / group to be provisioned + * @param password cleartext password to be provisioned + * @param changePwd whether password should be included for propagation attributes or not + * @param vAttrsToBeRemoved virtual attributes to be removed + * @param vAttrsToBeUpdated virtual attributes to be added + * @param membVAttrsToBeRemoved membership virtual attributes to be removed + * @param membVAttrsToBeUpdatedMap membership virtual attributes to be added + * @param enable whether user must be enabled or not + * @param deleteOnResource whether user / group must be deleted anyway from external resource or not + * @param propByRes operation to be performed per resource + * @return list of propagation tasks created + */ + protected List<PropagationTask> createTasks(final Subject<?, ?, ?> subject, + final String password, final boolean changePwd, + final Set<String> vAttrsToBeRemoved, final Map<String, AttrMod> vAttrsToBeUpdated, + final Set<String> membVAttrsToBeRemoved, final Map<String, AttrMod> membVAttrsToBeUpdatedMap, + final Boolean enable, final boolean deleteOnResource, final PropagationByResource propByRes) { + + LOG.debug("Provisioning subject {}:\n{}", subject, propByRes); + + final AttributableUtils attrUtils = attrUtilsFactory.getInstance(subject); + + if (!propByRes.get(ResourceOperation.CREATE).isEmpty() + && vAttrsToBeRemoved != null && vAttrsToBeUpdated != null) { + + connObjectUtils.retrieveVirAttrValues(subject, attrUtils); + + // update vAttrsToBeUpdated as well + for (VirAttr virAttr : subject.getVirAttrs()) { + final String schema = virAttr.getSchema().getKey(); + + final AttrMod attributeMod = new AttrMod(); + attributeMod.setSchema(schema); + attributeMod.getValuesToBeAdded().addAll(virAttr.getValues()); + + vAttrsToBeUpdated.put(schema, attributeMod); + } + } + + // Avoid duplicates - see javadoc + propByRes.purge(); + LOG.debug("After purge: {}", propByRes); + - final List<PropagationTask> tasks = new ArrayList<>(); ++ List<PropagationTask> tasks = new ArrayList<>(); + + for (ResourceOperation operation : ResourceOperation.values()) { + for (String resourceName : propByRes.get(operation)) { + final ExternalResource resource = resourceDAO.find(resourceName); + if (resource == null) { + LOG.error("Invalid resource name specified: {}, ignoring...", resourceName); + } else if (attrUtils.getMappingItems(resource, MappingPurpose.PROPAGATION).isEmpty()) { + LOG.warn("Requesting propagation for {} but no propagation mapping provided for {}", + attrUtils.getType(), resource); + } else { + PropagationTask task = entityFactory.newEntity(PropagationTask.class); + task.setResource(resource); + task.setObjectClassName(connObjectUtils.fromSubject(subject).getObjectClassValue()); + task.setSubjectType(attrUtils.getType()); + if (!deleteOnResource) { + task.setSubjectKey(subject.getKey()); + } + task.setPropagationOperation(operation); + task.setPropagationMode(resource.getPropagationMode()); + task.setOldAccountId(propByRes.getOldAccountId(resource.getKey())); + + Pair<String, Set<Attribute>> preparedAttrs = MappingUtils.prepareAttributes(attrUtils, subject, + password, changePwd, vAttrsToBeRemoved, vAttrsToBeUpdated, membVAttrsToBeRemoved, + membVAttrsToBeUpdatedMap, enable, resource); + task.setAccountId(preparedAttrs.getKey()); + + // Check if any of mandatory attributes (in the mapping) is missing or not received any value: + // if so, add special attributes that will be evaluated by PropagationTaskExecutor + List<String> mandatoryMissing = new ArrayList<>(); + List<String> mandatoryNullOrEmpty = new ArrayList<>(); + for (MappingItem item : attrUtils.getMappingItems(resource, MappingPurpose.PROPAGATION)) { + if (!item.isAccountid() + && JexlUtils.evaluateMandatoryCondition(item.getMandatoryCondition(), subject)) { + + Attribute attr = AttributeUtil.find(item.getExtAttrName(), preparedAttrs.getValue()); + if (attr == null) { + mandatoryMissing.add(item.getExtAttrName()); + } else if (attr.getValue() == null || attr.getValue().isEmpty()) { + mandatoryNullOrEmpty.add(item.getExtAttrName()); + } + } + } + if (!mandatoryMissing.isEmpty()) { + preparedAttrs.getValue().add(AttributeBuilder.build( + PropagationTaskExecutor.MANDATORY_MISSING_ATTR_NAME, mandatoryMissing)); + } + if (!mandatoryNullOrEmpty.isEmpty()) { + preparedAttrs.getValue().add(AttributeBuilder.build( + PropagationTaskExecutor.MANDATORY_NULL_OR_EMPTY_ATTR_NAME, mandatoryNullOrEmpty)); + } + + task.setAttributes(preparedAttrs.getValue()); + tasks.add(task); + + LOG.debug("PropagationTask created: {}", task); + } + } + } + + return tasks; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/22a9e12e/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java ---------------------------------------------------------------------- diff --cc core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java index 4fe02ba,0000000..3c910fa mode 100644,000000..100644 --- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java +++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java @@@ -1,134 -1,0 +1,134 @@@ +/* + * 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.rest.cxf.service; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.util.List; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.StreamingOutput; +import org.apache.syncope.common.lib.to.PagedResult; +import org.apache.syncope.common.lib.to.ReportExecTO; +import org.apache.syncope.common.lib.to.ReportTO; +import org.apache.syncope.common.lib.types.JobAction; +import org.apache.syncope.common.lib.types.JobStatusType; +import org.apache.syncope.common.lib.types.ReportExecExportFormat; +import org.apache.syncope.common.lib.wrap.ReportletConfClass; +import org.apache.syncope.common.rest.api.CollectionWrapper; +import org.apache.syncope.common.rest.api.RESTHeaders; +import org.apache.syncope.common.rest.api.beans.ListQuery; +import org.apache.syncope.common.rest.api.service.ReportService; +import org.apache.syncope.core.logic.ReportLogic; +import org.apache.syncope.core.persistence.api.entity.ReportExec; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class ReportServiceImpl extends AbstractServiceImpl implements ReportService { + + @Autowired + private ReportLogic logic; + + @Override + public Response create(final ReportTO reportTO) { + ReportTO createdReportTO = logic.create(reportTO); + URI location = uriInfo.getAbsolutePathBuilder().path(String.valueOf(createdReportTO.getKey())).build(); + return Response.created(location). + header(RESTHeaders.RESOURCE_ID, createdReportTO.getKey()). + build(); + } + + @Override + public void update(final Long reportKey, final ReportTO reportTO) { + reportTO.setKey(reportKey); + logic.update(reportTO); + } + + @Override + public PagedResult<ReportTO> list(final ListQuery listQuery) { + return buildPagedResult( + logic.list( + listQuery.getPage(), + listQuery.getSize(), + getOrderByClauses(listQuery.getOrderBy())), + listQuery.getPage(), + listQuery.getSize(), + logic.count()); + } + + @Override + public List<ReportletConfClass> getReportletConfClasses() { + return CollectionWrapper.wrap(logic.getReportletConfClasses(), ReportletConfClass.class); + } + + @Override + public ReportTO read(final Long reportKey) { + return logic.read(reportKey); + } + + @Override + public ReportExecTO readExecution(final Long executionKey) { + return logic.readExecution(executionKey); + } + + @Override + public Response exportExecutionResult(final Long executionKey, final ReportExecExportFormat fmt) { + final ReportExecExportFormat format = (fmt == null) ? ReportExecExportFormat.XML : fmt; + final ReportExec reportExec = logic.getAndCheckReportExec(executionKey); + StreamingOutput sout = new StreamingOutput() { + + @Override + public void write(final OutputStream os) throws IOException { + logic.exportExecutionResult(os, reportExec, format); + } + }; + String disposition = "attachment; filename=" + reportExec.getReport().getName() + "." + format.name(). + toLowerCase(); + return Response.ok(sout). + header(HttpHeaders.CONTENT_DISPOSITION, disposition). + build(); + } + + @Override + public ReportExecTO execute(final Long reportKey) { + return logic.execute(reportKey); + } + + @Override + public void delete(final Long reportKey) { + logic.delete(reportKey); + } + + @Override + public void deleteExecution(final Long executionKey) { + logic.deleteExecution(executionKey); + } + + @Override - public List<ReportExecTO> list(final JobStatusType type) { - return logic.list(type, ReportExecTO.class); ++ public List<ReportExecTO> listJobs(final JobStatusType type) { ++ return logic.listJobs(type, ReportExecTO.class); + } + + @Override - public void process(final JobAction action, final Long reportId) { - logic.process(action, reportId); ++ public void actionJob(final Long reportKey, final JobAction action) { ++ logic.actionJob(reportKey, action); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/22a9e12e/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/TaskServiceImpl.java ---------------------------------------------------------------------- diff --cc core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/TaskServiceImpl.java index 957891a,0000000..9da72aa mode 100644,000000..100644 --- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/TaskServiceImpl.java +++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/TaskServiceImpl.java @@@ -1,179 -1,0 +1,179 @@@ +/* + * 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.rest.cxf.service; + +import java.net.URI; +import java.util.List; +import javax.ws.rs.BadRequestException; +import javax.ws.rs.core.Response; +import org.apache.syncope.common.lib.to.AbstractTaskTO; +import org.apache.syncope.common.lib.to.BulkAction; +import org.apache.syncope.common.lib.to.BulkActionResult; +import org.apache.syncope.common.lib.to.PagedResult; +import org.apache.syncope.common.lib.to.PushTaskTO; +import org.apache.syncope.common.lib.to.ReportExecTO; +import org.apache.syncope.common.lib.to.SchedTaskTO; +import org.apache.syncope.common.lib.to.SyncTaskTO; +import org.apache.syncope.common.lib.to.TaskExecTO; +import org.apache.syncope.common.lib.types.JobAction; +import org.apache.syncope.common.lib.types.JobStatusType; +import org.apache.syncope.common.lib.types.PropagationTaskExecStatus; +import org.apache.syncope.common.lib.types.TaskType; +import org.apache.syncope.common.rest.api.RESTHeaders; +import org.apache.syncope.common.rest.api.beans.ListQuery; +import org.apache.syncope.common.rest.api.service.TaskService; +import org.apache.syncope.core.logic.TaskLogic; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class TaskServiceImpl extends AbstractServiceImpl implements TaskService { + + @Autowired + private TaskLogic logic; + + @Override + public <T extends SchedTaskTO> Response create(final T taskTO) { + T createdTask; + if (taskTO instanceof SyncTaskTO || taskTO instanceof PushTaskTO || taskTO instanceof SchedTaskTO) { + createdTask = logic.createSchedTask(taskTO); + } else { + throw new BadRequestException(); + } + + URI location = uriInfo.getAbsolutePathBuilder().path(String.valueOf(createdTask.getKey())).build(); + return Response.created(location). + header(RESTHeaders.RESOURCE_ID, createdTask.getKey()). + build(); + } + + @Override + public void delete(final Long taskKey) { + logic.delete(taskKey); + } + + @Override + public void deleteExecution(final Long executionKey) { + logic.deleteExecution(executionKey); + } + + @Override + public TaskExecTO execute(final Long taskKey, final boolean dryRun) { + return logic.execute(taskKey, dryRun); + } + + @SuppressWarnings("unchecked") + @Override + public <T extends AbstractTaskTO> PagedResult<T> list(final TaskType taskType, final ListQuery listQuery) { + return (PagedResult<T>) buildPagedResult( + logic.list( + taskType, + listQuery.getPage(), + listQuery.getSize(), + getOrderByClauses(listQuery.getOrderBy())), + listQuery.getPage(), + listQuery.getSize(), + logic.count(taskType)); + } + + @Override + public <T extends AbstractTaskTO> T read(final Long taskKey) { + return logic.read(taskKey); + } + + @Override + public TaskExecTO readExecution(final Long executionKey) { + return logic.readExecution(executionKey); + } + + @Override + public void report(final Long executionKey, final ReportExecTO reportExec) { + reportExec.setKey(executionKey); + logic.report( + executionKey, PropagationTaskExecStatus.fromString(reportExec.getStatus()), reportExec.getMessage()); + } + + @Override + public void update(final Long taskKey, final AbstractTaskTO taskTO) { + taskTO.setKey(taskKey); + if (taskTO instanceof SyncTaskTO) { + logic.updateSync((SyncTaskTO) taskTO); + } else if (taskTO instanceof SchedTaskTO) { + logic.updateSched((SchedTaskTO) taskTO); + } else { + throw new BadRequestException(); + } + } + + @Override + public BulkActionResult bulk(final BulkAction bulkAction) { + BulkActionResult result = new BulkActionResult(); + + switch (bulkAction.getOperation()) { + case DELETE: + for (String taskKey : bulkAction.getTargets()) { + try { + result.add(logic.delete(Long.valueOf(taskKey)).getKey(), BulkActionResult.Status.SUCCESS); + } catch (Exception e) { + LOG.error("Error performing delete for task {}", taskKey, e); + result.add(taskKey, BulkActionResult.Status.FAILURE); + } + } + break; + + case DRYRUN: + for (String taskKey : bulkAction.getTargets()) { + try { + logic.execute(Long.valueOf(taskKey), true); + result.add(taskKey, BulkActionResult.Status.SUCCESS); + } catch (Exception e) { + LOG.error("Error performing dryrun for task {}", taskKey, e); + result.add(taskKey, BulkActionResult.Status.FAILURE); + } + } + break; + + case EXECUTE: + for (String taskKey : bulkAction.getTargets()) { + try { + logic.execute(Long.valueOf(taskKey), false); + result.add(taskKey, BulkActionResult.Status.SUCCESS); + } catch (Exception e) { + LOG.error("Error performing execute for task {}", taskKey, e); + result.add(taskKey, BulkActionResult.Status.FAILURE); + } + } + break; + + default: + } + + return result; + } + + @Override - public List<TaskExecTO> list(final JobStatusType type) { - return logic.list(type, TaskExecTO.class); ++ public List<TaskExecTO> listJobs(final JobStatusType type) { ++ return logic.listJobs(type, TaskExecTO.class); + } + + @Override - public void process(final JobAction action, final Long taskId) { - logic.process(action, taskId); ++ public void actionJob(final Long taskKey, final JobAction action) { ++ logic.actionJob(taskKey, action); + } +}
