http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandler.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandler.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandler.java new file mode 100644 index 0000000..6f0e38e --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandler.java @@ -0,0 +1,308 @@ +/* + * 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.Collections; +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.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.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.AttributableUtil; +import org.apache.syncope.core.persistence.api.entity.AttributableUtilFactory; +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.role.RVirAttr; +import org.apache.syncope.core.persistence.api.entity.role.RVirAttrTemplate; +import org.apache.syncope.core.persistence.api.entity.role.Role; +import org.apache.syncope.core.persistence.api.entity.user.UVirAttr; +import org.apache.syncope.core.persistence.api.entity.user.UVirSchema; +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 ExternalResourceDAO resourceDAO; + + @Autowired + private VirSchemaDAO virSchemaDAO; + + @Autowired + private VirAttrDAO virAttrDAO; + + @Autowired + private UserDAO userDAO; + + @Autowired + private MembershipDAO membershipDAO; + + @Autowired + private AttributableUtilFactory attrUtilFactory; + + 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 RVirAttr) { + RVirAttrTemplate template = ((Role) attributable). + getAttrTemplate(RVirAttrTemplate.class, virSchema.getKey()); + if (template != null) { + ((RVirAttr) virAttr).setTemplate(template); + } + } else if (virAttr instanceof MVirAttr) { + MVirAttrTemplate template = + ((Membership) attributable).getRole(). + getAttrTemplate(MVirAttrTemplate.class, virSchema.getKey()); + if (template != null) { + ((MVirAttr) virAttr).setTemplate(template); + } + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public PropagationByResource fillVirtual(final Attributable attributable, + final Set<String> vAttrsToBeRemoved, final Set<AttrMod> vAttrsToBeUpdated, + final AttributableUtil attrUtil) { + + PropagationByResource propByRes = new PropagationByResource(); + + final Set<ExternalResource> externalResources = new HashSet<>(); + if (attributable instanceof Subject) { + externalResources.addAll(((Subject<?, ?, ?>) attributable).getResources()); + } + + if (attributable instanceof Membership) { + externalResources.clear(); + externalResources.addAll(((Membership) attributable).getUser().getResources()); + } + + // 1. virtual attributes to be removed + for (String vAttrToBeRemoved : vAttrsToBeRemoved) { + VirSchema virSchema = getVirSchema(vAttrToBeRemoved, attrUtil.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 : resourceDAO.findAll()) { + for (MappingItem mapItem : attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION)) { + if (virSchema.getKey().equals(mapItem.getIntAttrName()) + && mapItem.getIntMappingType() == attrUtil.virIntMappingType() + && externalResources.contains(resource)) { + + 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(), attrUtil.virSchemaClass()); + VirAttr virAttr = null; + if (virSchema != null) { + virAttr = attributable.getVirAttr(virSchema.getKey()); + if (virAttr == null) { + virAttr = attrUtil.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) { + for (ExternalResource resource : resourceDAO.findAll()) { + for (MappingItem mapItem : attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION)) { + if (virSchema.getKey().equals(mapItem.getIntAttrName()) + && mapItem.getIntMappingType() == attrUtil.virIntMappingType() + && externalResources.contains(resource)) { + + propByRes.add(ResourceOperation.UPDATE, resource.getKey()); + } + } + } + + 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 attrUtil attributable util. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void fillVirtual(final Attributable attributable, final Collection<AttrTO> vAttrs, + final AttributableUtil attrUtil) { + + for (AttrTO attributeTO : vAttrs) { + VirAttr virAttr = attributable.getVirAttr(attributeTO.getSchema()); + if (virAttr == null) { + VirSchema virSchema = getVirSchema(attributeTO.getSchema(), attrUtil.virSchemaClass()); + if (virSchema != null) { + virAttr = attrUtil.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, + attrUtilFactory.getInstance(AttributableType.USER)); + } + + /** + * SYNCOPE-501: build membership virtual attribute changes in case no other changes were made. + * + * @param key user key + * @param roleKey role 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 roleKey, final Long membershipKey, final Set<String> vAttrsToBeRemoved, + final Set<AttrMod> vAttrsToBeUpdated, final boolean isRemoval) { + + final Membership membership = membershipKey == null + ? userDAO.authFetch(key).getMembership(roleKey) + : membershipDAO.authFetch(membershipKey); + + return membership == null ? new PropagationByResource() : isRemoval + ? fillVirtual( + membership, + membership.getVirAttrs() == null + ? Collections.<String>emptySet() + : getAttrNames(membership.getVirAttrs()), + vAttrsToBeUpdated, + attrUtilFactory.getInstance(AttributableType.MEMBERSHIP)) + : fillVirtual( + membership, + vAttrsToBeRemoved, + vAttrsToBeUpdated, + attrUtilFactory.getInstance(AttributableType.MEMBERSHIP)); + } + + 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; + } + +}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/cache/DisabledVirAttrCache.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/cache/DisabledVirAttrCache.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/cache/DisabledVirAttrCache.java new file mode 100644 index 0000000..d1e0dda --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/cache/DisabledVirAttrCache.java @@ -0,0 +1,52 @@ +/* + * 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.cache; + +import org.apache.syncope.common.lib.types.AttributableType; +import org.apache.syncope.core.provisioning.api.cache.VirAttrCache; +import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheValue; + +/** + * Empty virtual attribute value cache implementation. + */ +public class DisabledVirAttrCache implements VirAttrCache { + + @Override + public void expire(final AttributableType type, final Long id, final String schemaName) { + // nothing to do + } + + @Override + public VirAttrCacheValue get(final AttributableType type, final Long id, final String schemaName) { + return null; + } + + @Override + public boolean isValidEntry(VirAttrCacheValue value) { + return false; + } + + @Override + public void put( + final AttributableType type, final Long id, final String schemaName, final VirAttrCacheValue value) { + + // nothing to do + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/cache/MemoryVirAttrCache.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/cache/MemoryVirAttrCache.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/cache/MemoryVirAttrCache.java new file mode 100644 index 0000000..d746ae4 --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/cache/MemoryVirAttrCache.java @@ -0,0 +1,151 @@ +/* + * 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.cache; + +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.apache.syncope.common.lib.types.AttributableType; +import org.apache.syncope.core.provisioning.api.cache.VirAttrCache; +import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheKey; +import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheValue; + +/** + * In-memory (HashMap) virtual attribute value cache implementation. + */ +public class MemoryVirAttrCache implements VirAttrCache { + + /** + * Elapsed time in seconds. + */ + protected int ttl; + + /** + * Max cache size. + */ + protected int maxCacheSize; + + /** + * Cache entries. + */ + protected final Map<VirAttrCacheKey, VirAttrCacheValue> cache = new HashMap<VirAttrCacheKey, VirAttrCacheValue>(); + + public MemoryVirAttrCache(final int ttl, final int maxCacheSize) { + this.ttl = ttl; + this.maxCacheSize = maxCacheSize; + } + + /** + * Cache virtual attribute values. + * + * @param type user or role + * @param key user or role id + * @param schemaName virtual attribute name + * @param value virtual attribute values + */ + @Override + public void put( + final AttributableType type, + final Long key, + final String schemaName, + final VirAttrCacheValue value) { + + synchronized (cache) { + // this operations (retrieve cache space and put entry on) have to be thread safe. + if (this.cache.size() >= this.maxCacheSize) { + free(); + } + + cache.put(new VirAttrCacheKey(type, key, schemaName), value); + } + } + + /** + * Retrieve cached value. Return null in case of virtual attribute not cached. + * + * @param type user or role + * @param id user or role id + * @param schemaName virtual attribute schema name. + * @return cached values or null if virtual attribute is not cached. + */ + @Override + public VirAttrCacheValue get(final AttributableType type, final Long id, final String schemaName) { + return cache.get(new VirAttrCacheKey(type, id, schemaName)); + } + + /** + * Force entry expiring. + * + * @param type user or role + * @param id user or role id + * @param schemaName virtual attribute schema name + */ + @Override + public void expire(final AttributableType type, final Long id, final String schemaName) { + final VirAttrCacheValue value = cache.get(new VirAttrCacheKey(type, id, schemaName)); + if (isValidEntry(value)) { + synchronized (cache) { + value.forceExpiring(); + } + } + } + + /** + * Remove expired entries if exist. If required, one entry at least (the latest recently used) will be taken off. + * This method is not thread safe: the caller have to take care to synchronize the call. + */ + private void free() { + final Set<VirAttrCacheKey> toBeRemoved = new HashSet<VirAttrCacheKey>(); + + Map.Entry<VirAttrCacheKey, VirAttrCacheValue> latest = null; + + for (Map.Entry<VirAttrCacheKey, VirAttrCacheValue> entry : cache.entrySet()) { + if (isValidEntry(entry.getValue())) { + final Date date = entry.getValue().getLastAccessDate(); + if (latest == null || latest.getValue().getLastAccessDate().after(date)) { + latest = entry; + } + } else { + toBeRemoved.add(entry.getKey()); + } + } + + if (toBeRemoved.isEmpty() && latest != null) { + // remove the oldest entry + cache.remove(latest.getKey()); + } else { + // remove expired entries + cache.keySet().removeAll(toBeRemoved); + } + } + + /** + * Cache entry is valid if and only if value exist and it is not expired. + * + * @param value cache entry value. + * @return TRUE if the value is valid; FALSE otherwise. + */ + @Override + public boolean isValidEntry(final VirAttrCacheValue value) { + final Date expiringDate = new Date(value == null ? 0 : value.getCreationDate().getTime() + ttl * 1000); + return expiringDate.after(new Date()); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAttributableDataBinder.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAttributableDataBinder.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAttributableDataBinder.java new file mode 100644 index 0000000..fb8d8d4 --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAttributableDataBinder.java @@ -0,0 +1,779 @@ +/* + * 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.data; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.SyncopeClientCompositeException; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.mod.AbstractAttributableMod; +import org.apache.syncope.common.lib.mod.AbstractSubjectMod; +import org.apache.syncope.common.lib.mod.AttrMod; +import org.apache.syncope.common.lib.to.AbstractAttributableTO; +import org.apache.syncope.common.lib.to.AbstractSubjectTO; +import org.apache.syncope.common.lib.to.AttrTO; +import org.apache.syncope.common.lib.types.AttributableType; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.IntMappingType; +import org.apache.syncope.common.lib.types.MappingPurpose; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidPlainAttrValueException; +import org.apache.syncope.core.persistence.api.dao.DerAttrDAO; +import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.MembershipDAO; +import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO; +import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.PolicyDAO; +import org.apache.syncope.core.persistence.api.dao.RoleDAO; +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.AttributableUtil; +import org.apache.syncope.core.persistence.api.entity.AttributableUtilFactory; +import org.apache.syncope.core.persistence.api.entity.DerAttr; +import org.apache.syncope.core.persistence.api.entity.DerSchema; +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.PlainAttr; +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.Schema; +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.MDerAttr; +import org.apache.syncope.core.persistence.api.entity.membership.MDerAttrTemplate; +import org.apache.syncope.core.persistence.api.entity.membership.MPlainAttr; +import org.apache.syncope.core.persistence.api.entity.membership.MPlainAttrTemplate; +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.role.RDerAttr; +import org.apache.syncope.core.persistence.api.entity.role.RDerAttrTemplate; +import org.apache.syncope.core.persistence.api.entity.role.RPlainAttr; +import org.apache.syncope.core.persistence.api.entity.role.RPlainAttrTemplate; +import org.apache.syncope.core.persistence.api.entity.role.RVirAttrTemplate; +import org.apache.syncope.core.persistence.api.entity.role.Role; +import org.apache.syncope.core.persistence.api.entity.user.UDerAttr; +import org.apache.syncope.core.persistence.api.entity.user.UDerSchema; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.UPlainSchema; +import org.apache.syncope.common.lib.types.PropagationByResource; +import org.apache.syncope.core.provisioning.java.VirAttrHandler; +import org.apache.syncope.core.misc.MappingUtil; +import org.apache.syncope.core.misc.jexl.JexlUtil; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +abstract class AbstractAttributableDataBinder { + + /** + * Logger. + */ + protected static final Logger LOG = LoggerFactory.getLogger(AbstractAttributableDataBinder.class); + + @Autowired + protected RoleDAO roleDAO; + + @Autowired + protected PlainSchemaDAO plainSchemaDAO; + + @Autowired + protected DerSchemaDAO derSchemaDAO; + + @Autowired + protected VirSchemaDAO virSchemaDAO; + + @Autowired + protected PlainAttrDAO plainAttrDAO; + + @Autowired + protected DerAttrDAO derAttrDAO; + + @Autowired + protected VirAttrDAO virAttrDAO; + + @Autowired + protected PlainAttrValueDAO plainAttrValueDAO; + + @Autowired + protected UserDAO userDAO; + + @Autowired + protected ExternalResourceDAO resourceDAO; + + @Autowired + protected MembershipDAO membershipDAO; + + @Autowired + protected PolicyDAO policyDAO; + + @Autowired + protected EntityFactory entityFactory; + + @Autowired + protected AttributableUtilFactory attrUtilFactory; + + @Autowired + protected VirAttrHandler virtAttrHander; + + @SuppressWarnings("unchecked") + protected <T extends Schema> T getSchema(final String schemaName, final Class<T> reference) { + T result = null; + + if (PlainSchema.class.isAssignableFrom(reference)) { + result = (T) getPlainSchema(schemaName, (Class<? extends PlainSchema>) reference); + } else if (DerSchema.class.isAssignableFrom(reference)) { + result = (T) getDerSchema(schemaName, (Class<? extends DerSchema>) reference); + } else if (VirSchema.class.isAssignableFrom(reference)) { + result = (T) virtAttrHander.getVirSchema(schemaName, (Class<? extends VirSchema>) reference); + } + + return result; + } + + protected <T extends PlainSchema> T getPlainSchema(final String schemaName, final Class<T> reference) { + T schema = null; + if (StringUtils.isNotBlank(schemaName)) { + schema = plainSchemaDAO.find(schemaName, reference); + + // safely ignore invalid schemas from AttrTO + // see http://code.google.com/p/syncope/issues/detail?id=17 + if (schema == null) { + LOG.debug("Ignoring invalid schema {}", schemaName); + } else if (schema.isReadonly()) { + schema = null; + + LOG.debug("Ignoring readonly schema {}", schemaName); + } + } + + return schema; + } + + private <T extends DerSchema> T getDerSchema(final String derSchemaName, final Class<T> reference) { + T derivedSchema = null; + if (StringUtils.isNotBlank(derSchemaName)) { + derivedSchema = derSchemaDAO.find(derSchemaName, reference); + if (derivedSchema == null) { + LOG.debug("Ignoring invalid derived schema {}", derSchemaName); + } + } + + return derivedSchema; + } + + protected void fillAttribute(final List<String> values, final AttributableUtil attributableUtil, + final PlainSchema schema, final PlainAttr attr, final SyncopeClientException invalidValues) { + + // if schema is multivalue, all values are considered for addition; + // otherwise only the fist one - if provided - is considered + List<String> valuesProvided = schema.isMultivalue() + ? values + : (values.isEmpty() + ? Collections.<String>emptyList() + : Collections.singletonList(values.iterator().next())); + + for (String value : valuesProvided) { + if (value == null || value.isEmpty()) { + LOG.debug("Null value for {}, ignoring", schema.getKey()); + } else { + try { + attr.addValue(value, attributableUtil); + } catch (InvalidPlainAttrValueException e) { + LOG.warn("Invalid value for attribute " + schema.getKey() + ": " + value, e); + + invalidValues.getElements().add(schema.getKey() + ": " + value + " - " + e.getMessage()); + } + } + } + } + + private boolean evaluateMandatoryCondition(final AttributableUtil attrUtil, final ExternalResource resource, + final Attributable<?, ?, ?> attributable, final String intAttrName, final IntMappingType intMappingType) { + + boolean result = false; + + final List<MappingItem> mappings = MappingUtil.getMatchingMappingItems( + attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION), intAttrName, intMappingType); + for (Iterator<MappingItem> itor = mappings.iterator(); itor.hasNext() && !result;) { + final MappingItem mapping = itor.next(); + result |= JexlUtil.evaluateMandatoryCondition(mapping.getMandatoryCondition(), attributable); + } + + return result; + } + + private boolean evaluateMandatoryCondition(final AttributableUtil attrUtil, + final Attributable<?, ?, ?> attributable, final String intAttrName, final IntMappingType intMappingType) { + + boolean result = false; + + if (attributable instanceof Subject) { + for (Iterator<? extends ExternalResource> itor = ((Subject<?, ?, ?>) attributable).getResources().iterator(); + itor.hasNext() && !result;) { + + final ExternalResource resource = itor.next(); + if (resource.isEnforceMandatoryCondition()) { + result |= evaluateMandatoryCondition(attrUtil, resource, attributable, intAttrName, intMappingType); + } + } + } + + return result; + } + + private SyncopeClientException checkMandatory(final AttributableUtil attrUtil, + final Attributable<?, ?, ?> attributable) { + + SyncopeClientException reqValMissing = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing); + + // Check if there is some mandatory schema defined for which no value has been provided + List<? extends PlainSchema> plainSchemas; + switch (attrUtil.getType()) { + case ROLE: + plainSchemas = ((Role) attributable).getAttrTemplateSchemas(RPlainAttrTemplate.class); + break; + + case MEMBERSHIP: + plainSchemas = ((Membership) attributable).getRole().getAttrTemplateSchemas(MPlainAttrTemplate.class); + break; + + case USER: + default: + plainSchemas = plainSchemaDAO.findAll(attrUtil.plainSchemaClass()); + } + for (PlainSchema schema : plainSchemas) { + if (attributable.getPlainAttr(schema.getKey()) == null + && !schema.isReadonly() + && (JexlUtil.evaluateMandatoryCondition(schema.getMandatoryCondition(), attributable) + || evaluateMandatoryCondition(attrUtil, attributable, schema.getKey(), + attrUtil.intMappingType()))) { + + LOG.error("Mandatory schema " + schema.getKey() + " not provided with values"); + + reqValMissing.getElements().add(schema.getKey()); + } + } + + List<? extends DerSchema> derSchemas; + switch (attrUtil.getType()) { + case ROLE: + derSchemas = ((Role) attributable).getAttrTemplateSchemas(RDerAttrTemplate.class); + break; + + case MEMBERSHIP: + derSchemas = ((Membership) attributable).getRole().getAttrTemplateSchemas(MDerAttrTemplate.class); + break; + + case USER: + default: + derSchemas = derSchemaDAO.findAll(attrUtil.derSchemaClass()); + } + for (DerSchema derSchema : derSchemas) { + if (attributable.getDerAttr(derSchema.getKey()) == null + && evaluateMandatoryCondition(attrUtil, attributable, derSchema.getKey(), + attrUtil.derIntMappingType())) { + + LOG.error("Mandatory derived schema " + derSchema.getKey() + " does not evaluate to any value"); + + reqValMissing.getElements().add(derSchema.getKey()); + } + } + + List<? extends VirSchema> virSchemas; + switch (attrUtil.getType()) { + case ROLE: + virSchemas = ((Role) attributable).getAttrTemplateSchemas(RVirAttrTemplate.class); + break; + + case MEMBERSHIP: + virSchemas = ((Membership) attributable).getRole().getAttrTemplateSchemas(MVirAttrTemplate.class); + break; + + case USER: + default: + virSchemas = virSchemaDAO.findAll(attrUtil.virSchemaClass()); + } + for (VirSchema virSchema : virSchemas) { + if (attributable.getVirAttr(virSchema.getKey()) == null + && !virSchema.isReadonly() + && evaluateMandatoryCondition(attrUtil, attributable, virSchema.getKey(), + attrUtil.virIntMappingType())) { + + LOG.error("Mandatory virtual schema " + virSchema.getKey() + " not provided with values"); + + reqValMissing.getElements().add(virSchema.getKey()); + } + } + + return reqValMissing; + } + + private void setPlainAttrSchema(final Attributable<?, ?, ?> attributable, + final PlainAttr attr, final PlainSchema schema) { + + if (attr instanceof UPlainAttr) { + ((UPlainAttr) attr).setSchema((UPlainSchema) schema); + } else if (attr instanceof RPlainAttr) { + RPlainAttrTemplate template = + ((Role) attributable).getAttrTemplate(RPlainAttrTemplate.class, schema.getKey()); + if (template != null) { + ((RPlainAttr) attr).setTemplate(template); + } + } else if (attr instanceof MPlainAttr) { + MPlainAttrTemplate template = ((Membership) attributable).getRole(). + getAttrTemplate(MPlainAttrTemplate.class, schema.getKey()); + if (template != null) { + ((MPlainAttr) attr).setTemplate(template); + } + } + } + + private void setDerAttrSchema(final Attributable<?, ?, ?> attributable, + final DerAttr derAttr, final DerSchema derSchema) { + + if (derAttr instanceof UDerAttr) { + ((UDerAttr) derAttr).setSchema((UDerSchema) derSchema); + } else if (derAttr instanceof RDerAttr) { + RDerAttrTemplate template = ((Role) attributable). + getAttrTemplate(RDerAttrTemplate.class, derSchema.getKey()); + if (template != null) { + ((RDerAttr) derAttr).setTemplate(template); + } + } else if (derAttr instanceof MDerAttr) { + MDerAttrTemplate template = ((Membership) attributable).getRole(). + getAttrTemplate(MDerAttrTemplate.class, derSchema.getKey()); + if (template != null) { + ((MDerAttr) derAttr).setTemplate(template); + } + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + protected PropagationByResource fill(final Attributable attributable, + final AbstractAttributableMod attributableMod, final AttributableUtil attrUtil, + final SyncopeClientCompositeException scce) { + + PropagationByResource propByRes = new PropagationByResource(); + + SyncopeClientException invalidValues = SyncopeClientException.build(ClientExceptionType.InvalidValues); + + if (attributable instanceof Subject && attributableMod instanceof AbstractSubjectMod) { + // 1. resources to be removed + for (String resourceToBeRemoved : ((AbstractSubjectMod) attributableMod).getResourcesToRemove()) { + ExternalResource resource = resourceDAO.find(resourceToBeRemoved); + if (resource != null) { + propByRes.add(ResourceOperation.DELETE, resource.getKey()); + ((Subject<?, ?, ?>) attributable).removeResource(resource); + } + } + + LOG.debug("Resources to be removed:\n{}", propByRes); + + // 2. resources to be added + for (String resourceToBeAdded : ((AbstractSubjectMod) attributableMod).getResourcesToAdd()) { + ExternalResource resource = resourceDAO.find(resourceToBeAdded); + if (resource != null) { + propByRes.add(ResourceOperation.CREATE, resource.getKey()); + ((Subject<?, ?, ?>) attributable).addResource(resource); + } + } + + LOG.debug("Resources to be added:\n{}", propByRes); + } + + // 3. attributes to be removed + for (String attributeToBeRemoved : attributableMod.getPlainAttrsToRemove()) { + PlainSchema schema = getPlainSchema(attributeToBeRemoved, attrUtil.plainSchemaClass()); + if (schema != null) { + PlainAttr attr = attributable.getPlainAttr(schema.getKey()); + if (attr == null) { + LOG.debug("No attribute found for schema {}", schema); + } else { + String newValue = null; + for (AttrMod mod : attributableMod.getPlainAttrsToUpdate()) { + if (schema.getKey().equals(mod.getSchema())) { + newValue = mod.getValuesToBeAdded().get(0); + } + } + + if (!schema.isUniqueConstraint() + || (!attr.getUniqueValue().getStringValue().equals(newValue))) { + + attributable.removePlainAttr(attr); + plainAttrDAO.delete(attr.getKey(), attrUtil.plainAttrClass()); + } + } + + if (attributable instanceof Subject) { + for (ExternalResource resource : resourceDAO.findAll()) { + for (MappingItem mapItem : attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION)) { + if (schema.getKey().equals(mapItem.getIntAttrName()) + && mapItem.getIntMappingType() == attrUtil.intMappingType() + && ((Subject<?, ?, ?>) attributable).getResources().contains(resource)) { + + propByRes.add(ResourceOperation.UPDATE, resource.getKey()); + + if (mapItem.isAccountid() && attr != null + && !attr.getValuesAsStrings().isEmpty()) { + + propByRes.addOldAccountId(resource.getKey(), + attr.getValuesAsStrings().iterator().next()); + } + } + } + } + } + } + } + + LOG.debug("Attributes to be removed:\n{}", propByRes); + + // 4. attributes to be updated + for (AttrMod attributeMod : attributableMod.getPlainAttrsToUpdate()) { + PlainSchema schema = getPlainSchema(attributeMod.getSchema(), attrUtil.plainSchemaClass()); + PlainAttr attr = null; + if (schema != null) { + attr = attributable.getPlainAttr(schema.getKey()); + if (attr == null) { + attr = attrUtil.newPlainAttr(); + setPlainAttrSchema(attributable, attr, schema); + if (attr.getSchema() == null) { + LOG.debug("Ignoring {} because no valid schema or template was found", attributeMod); + } else { + attr.setOwner(attributable); + attributable.addPlainAttr(attr); + } + } + } + + if (schema != null && attr != null && attr.getSchema() != null) { + if (attributable instanceof Subject) { + for (ExternalResource resource : resourceDAO.findAll()) { + for (MappingItem mapItem : attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION)) { + if (schema.getKey().equals(mapItem.getIntAttrName()) + && mapItem.getIntMappingType() == attrUtil.intMappingType() + && ((Subject<?, ?, ?>) attributable).getResources().contains(resource)) { + + propByRes.add(ResourceOperation.UPDATE, resource.getKey()); + } + } + } + } + + // 1.1 remove values + Set<Long> valuesToBeRemoved = new HashSet<>(); + for (String valueToBeRemoved : attributeMod.getValuesToBeRemoved()) { + if (attr.getSchema().isUniqueConstraint()) { + if (attr.getUniqueValue() != null + && valueToBeRemoved.equals(attr.getUniqueValue().getValueAsString())) { + + valuesToBeRemoved.add(attr.getUniqueValue().getKey()); + } + } else { + for (PlainAttrValue mav : attr.getValues()) { + if (valueToBeRemoved.equals(mav.getValueAsString())) { + valuesToBeRemoved.add(mav.getKey()); + } + } + } + } + for (Long attributeValueId : valuesToBeRemoved) { + plainAttrValueDAO.delete(attributeValueId, attrUtil.plainAttrValueClass()); + } + + // 1.2 add values + List<String> valuesToBeAdded = attributeMod.getValuesToBeAdded(); + if (valuesToBeAdded != null && !valuesToBeAdded.isEmpty() + && (!schema.isUniqueConstraint() || attr.getUniqueValue() == null + || !valuesToBeAdded.iterator().next().equals(attr.getUniqueValue().getValueAsString()))) { + + fillAttribute(attributeMod.getValuesToBeAdded(), attrUtil, schema, attr, invalidValues); + } + + // if no values are in, the attribute can be safely removed + if (attr.getValuesAsStrings().isEmpty()) { + plainAttrDAO.delete(attr); + } + } + } + + if (!invalidValues.isEmpty()) { + scce.addException(invalidValues); + } + + LOG.debug("Attributes to be updated:\n{}", propByRes); + + // 5. derived attributes to be removed + for (String derAttrToBeRemoved : attributableMod.getDerAttrsToRemove()) { + DerSchema derSchema = getDerSchema(derAttrToBeRemoved, attrUtil.derSchemaClass()); + if (derSchema != null) { + DerAttr derAttr = attributable.getDerAttr(derSchema.getKey()); + if (derAttr == null) { + LOG.debug("No derived attribute found for schema {}", derSchema.getKey()); + } else { + derAttrDAO.delete(derAttr); + } + + if (attributable instanceof Subject) { + for (ExternalResource resource : resourceDAO.findAll()) { + for (MappingItem mapItem : attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION)) { + if (derSchema.getKey().equals(mapItem.getIntAttrName()) + && mapItem.getIntMappingType() == attrUtil.derIntMappingType() + && ((Subject<?, ?, ?>) attributable).getResources().contains(resource)) { + + propByRes.add(ResourceOperation.UPDATE, resource.getKey()); + + if (mapItem.isAccountid() && derAttr != null + && !derAttr.getValue(attributable.getPlainAttrs()).isEmpty()) { + + propByRes.addOldAccountId(resource.getKey(), + derAttr.getValue(attributable.getPlainAttrs())); + } + } + } + } + } + } + } + + LOG.debug("Derived attributes to be removed:\n{}", propByRes); + + // 6. derived attributes to be added + for (String derAttrToBeAdded : attributableMod.getDerAttrsToAdd()) { + DerSchema derSchema = getDerSchema(derAttrToBeAdded, attrUtil.derSchemaClass()); + if (derSchema != null) { + if (attributable instanceof Subject) { + for (ExternalResource resource : resourceDAO.findAll()) { + for (MappingItem mapItem : attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION)) { + if (derSchema.getKey().equals(mapItem.getIntAttrName()) + && mapItem.getIntMappingType() == attrUtil.derIntMappingType() + && ((Subject<?, ?, ?>) attributable).getResources().contains(resource)) { + + propByRes.add(ResourceOperation.UPDATE, resource.getKey()); + } + } + } + } + + DerAttr derAttr = attrUtil.newDerAttr(); + setDerAttrSchema(attributable, derAttr, derSchema); + if (derAttr.getSchema() == null) { + LOG.debug("Ignoring {} because no valid schema or template was found", derAttrToBeAdded); + } else { + derAttr.setOwner(attributable); + attributable.addDerAttr(derAttr); + } + } + } + + LOG.debug("Derived attributes to be added:\n{}", propByRes); + + // 7. virtual attributes: for users and roles this is delegated to PropagationManager + if (AttributableType.USER != attrUtil.getType() && AttributableType.ROLE != attrUtil.getType()) { + virtAttrHander.fillVirtual(attributable, attributableMod.getVirAttrsToRemove(), + attributableMod.getVirAttrsToUpdate(), attrUtil); + } + + // Finally, check if mandatory values are missing + SyncopeClientException requiredValuesMissing = checkMandatory(attrUtil, attributable); + if (!requiredValuesMissing.isEmpty()) { + scce.addException(requiredValuesMissing); + } + + // Throw composite exception if there is at least one element set in the composing exceptions + if (scce.hasExceptions()) { + throw scce; + } + + return propByRes; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + protected void fill(final Attributable attributable, final AbstractAttributableTO attributableTO, + final AttributableUtil attrUtil, final SyncopeClientCompositeException scce) { + + // 1. attributes + SyncopeClientException invalidValues = SyncopeClientException.build(ClientExceptionType.InvalidValues); + + // Only consider attributeTO with values + for (AttrTO attributeTO : attributableTO.getPlainAttrs()) { + if (attributeTO.getValues() != null && !attributeTO.getValues().isEmpty()) { + PlainSchema schema = getPlainSchema(attributeTO.getSchema(), attrUtil.plainSchemaClass()); + + if (schema != null) { + PlainAttr attr = attributable.getPlainAttr(schema.getKey()); + if (attr == null) { + attr = attrUtil.newPlainAttr(); + setPlainAttrSchema(attributable, attr, schema); + } + if (attr.getSchema() == null) { + LOG.debug("Ignoring {} because no valid schema or template was found", attributeTO); + } else { + fillAttribute(attributeTO.getValues(), attrUtil, schema, attr, invalidValues); + + if (!attr.getValuesAsStrings().isEmpty()) { + attributable.addPlainAttr(attr); + attr.setOwner(attributable); + } + } + } + } + } + + if (!invalidValues.isEmpty()) { + scce.addException(invalidValues); + } + + // 2. derived attributes + for (AttrTO attributeTO : attributableTO.getDerAttrs()) { + DerSchema derSchema = getDerSchema(attributeTO.getSchema(), attrUtil.derSchemaClass()); + + if (derSchema != null) { + DerAttr derAttr = attrUtil.newDerAttr(); + setDerAttrSchema(attributable, derAttr, derSchema); + if (derAttr.getSchema() == null) { + LOG.debug("Ignoring {} because no valid schema or template was found", attributeTO); + } else { + derAttr.setOwner(attributable); + attributable.addDerAttr(derAttr); + } + } + } + + // 3. user and role virtual attributes will be evaluated by the propagation manager only (if needed). + if (AttributableType.USER == attrUtil.getType() + || AttributableType.ROLE == attrUtil.getType()) { + + for (AttrTO vattrTO : attributableTO.getVirAttrs()) { + VirSchema virSchema = virtAttrHander.getVirSchema(vattrTO.getSchema(), attrUtil.virSchemaClass()); + + if (virSchema != null) { + VirAttr virAttr = attrUtil.newVirAttr(); + virtAttrHander.setVirAttrSchema(attributable, virAttr, virSchema); + if (virAttr.getSchema() == null) { + LOG.debug("Ignoring {} because no valid schema or template was found", vattrTO); + } else { + virAttr.setOwner(attributable); + attributable.addVirAttr(virAttr); + } + } + } + } + + virtAttrHander.fillVirtual(attributable, attributableTO.getVirAttrs(), attrUtil); + + // 4. resources + if (attributable instanceof Subject && attributableTO instanceof AbstractSubjectTO) { + for (String resourceName : ((AbstractSubjectTO) attributableTO).getResources()) { + ExternalResource resource = resourceDAO.find(resourceName); + + if (resource != null) { + ((Subject<?, ?, ?>) attributable).addResource(resource); + } + } + } + + SyncopeClientException requiredValuesMissing = checkMandatory(attrUtil, attributable); + if (!requiredValuesMissing.isEmpty()) { + scce.addException(requiredValuesMissing); + } + + // Throw composite exception if there is at least one element set in the composing exceptions + if (scce.hasExceptions()) { + throw scce; + } + } + + protected void fillTO(final AbstractAttributableTO attributableTO, + final Collection<? extends PlainAttr> attrs, + final Collection<? extends DerAttr> derAttrs, + final Collection<? extends VirAttr> virAttrs, + final Collection<? extends ExternalResource> resources) { + + AttrTO attributeTO; + for (PlainAttr attr : attrs) { + attributeTO = new AttrTO(); + attributeTO.setSchema(attr.getSchema().getKey()); + attributeTO.getValues().addAll(attr.getValuesAsStrings()); + attributeTO.setReadonly(attr.getSchema().isReadonly()); + + attributableTO.getPlainAttrs().add(attributeTO); + } + + for (DerAttr derAttr : derAttrs) { + attributeTO = new AttrTO(); + attributeTO.setSchema(derAttr.getSchema().getKey()); + attributeTO.getValues().add(derAttr.getValue(attrs)); + attributeTO.setReadonly(true); + + attributableTO.getDerAttrs().add(attributeTO); + } + + for (VirAttr virAttr : virAttrs) { + attributeTO = new AttrTO(); + attributeTO.setSchema(virAttr.getSchema().getKey()); + attributeTO.getValues().addAll(virAttr.getValues()); + attributeTO.setReadonly(virAttr.getSchema().isReadonly()); + + attributableTO.getVirAttrs().add(attributeTO); + } + + if (attributableTO instanceof AbstractSubjectTO) { + for (ExternalResource resource : resources) { + ((AbstractSubjectTO) attributableTO).getResources().add(resource.getKey()); + } + } + } + + protected Map<String, String> getAccountIds(final Subject<?, ?, ?> subject, final AttributableType type) { + Map<String, String> accountIds = new HashMap<>(); + + for (ExternalResource resource : subject.getResources()) { + if ((type == AttributableType.USER && resource.getUmapping() != null) + || (type == AttributableType.ROLE && resource.getRmapping() != null)) { + + MappingItem accountIdItem = + attrUtilFactory.getInstance(type).getAccountIdItem(resource); + if (accountIdItem == null) { + throw new NotFoundException( + "AccountId mapping for " + type + " " + subject.getKey() + + " on resource '" + resource.getKey() + "'"); + } + + accountIds.put(resource.getKey(), MappingUtil.getAccountIdValue(subject, resource, accountIdItem)); + } + } + + return accountIds; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConfigurationDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConfigurationDataBinderImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConfigurationDataBinderImpl.java new file mode 100644 index 0000000..f1d9020 --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConfigurationDataBinderImpl.java @@ -0,0 +1,79 @@ +/* + * 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.data; + +import org.apache.syncope.core.provisioning.api.data.ConfigurationDataBinder; +import java.util.Collections; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.AttrTO; +import org.apache.syncope.common.lib.to.ConfTO; +import org.apache.syncope.common.lib.types.AttributableType; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.conf.CPlainAttr; +import org.apache.syncope.core.persistence.api.entity.conf.CPlainSchema; +import org.apache.syncope.core.persistence.api.entity.conf.Conf; +import org.springframework.stereotype.Component; + +@Component +public class ConfigurationDataBinderImpl extends AbstractAttributableDataBinder implements ConfigurationDataBinder { + + @Override + public ConfTO getConfTO(final Conf conf) { + final ConfTO confTO = new ConfTO(); + confTO.setKey(conf.getKey()); + + fillTO(confTO, conf.getPlainAttrs(), + conf.getDerAttrs(), conf.getVirAttrs(), Collections.<ExternalResource>emptySet()); + + return confTO; + } + + @Override + public AttrTO getAttrTO(final CPlainAttr attr) { + final AttrTO attributeTO = new AttrTO(); + attributeTO.setSchema(attr.getSchema().getKey()); + attributeTO.getValues().addAll(attr.getValuesAsStrings()); + attributeTO.setReadonly(attr.getSchema().isReadonly()); + + return attributeTO; + } + + @Override + public CPlainAttr getAttribute(final AttrTO attributeTO) { + CPlainSchema schema = getPlainSchema(attributeTO.getSchema(), CPlainSchema.class); + if (schema == null) { + throw new NotFoundException("Conf schema " + attributeTO.getSchema()); + } else { + SyncopeClientException invalidValues = SyncopeClientException.build(ClientExceptionType.InvalidValues); + + CPlainAttr attr = entityFactory.newEntity(CPlainAttr.class); + attr.setSchema(schema); + fillAttribute(attributeTO.getValues(), attrUtilFactory.getInstance(AttributableType.CONFIGURATION), + schema, attr, invalidValues); + + if (!invalidValues.isEmpty()) { + throw invalidValues; + } + return attr; + } + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java new file mode 100644 index 0000000..fbb687e --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java @@ -0,0 +1,245 @@ +/* + * 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.data; + +import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.ConnInstanceTO; +import org.apache.syncope.common.lib.to.ConnPoolConfTO; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.ConnConfPropSchema; +import org.apache.syncope.common.lib.types.ConnConfProperty; +import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO; +import org.apache.syncope.core.persistence.api.entity.ConnInstance; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.provisioning.api.ConnIdBundleManager; +import org.apache.syncope.core.provisioning.api.ConnPoolConfUtil; +import org.identityconnectors.framework.api.ConfigurationProperties; +import org.identityconnectors.framework.api.ConfigurationProperty; +import org.identityconnectors.framework.impl.api.ConfigurationPropertyImpl; +import org.apache.syncope.core.misc.spring.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder { + + private static final String[] IGNORE_PROPERTIES = { "key", "poolConf" }; + + @Autowired + private ConnIdBundleManager connIdBundleManager; + + @Autowired + private ConnInstanceDAO connInstanceDAO; + + @Autowired + private EntityFactory entityFactory; + + @Override + public Set<ConnConfProperty> mergeConnConfProperties(final Set<ConnConfProperty> primary, + final Set<ConnConfProperty> secondary) { + + final Set<ConnConfProperty> conf = new HashSet<>(); + + // to be used to control managed prop (needed by overridden mechanism) + final Set<String> propertyNames = new HashSet<>(); + + // get overridden connector configuration properties + for (ConnConfProperty prop : primary) { + if (!propertyNames.contains(prop.getSchema().getName())) { + conf.add(prop); + propertyNames.add(prop.getSchema().getName()); + } + } + + // get connector configuration properties + for (ConnConfProperty prop : secondary) { + if (!propertyNames.contains(prop.getSchema().getName())) { + conf.add(prop); + propertyNames.add(prop.getSchema().getName()); + } + } + + return conf; + } + + @Override + public ConnInstance getConnInstance(final ConnInstanceTO connInstanceTO) { + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing); + + if (connInstanceTO.getLocation() == null) { + sce.getElements().add("location"); + } + + if (connInstanceTO.getBundleName() == null) { + sce.getElements().add("bundlename"); + } + + if (connInstanceTO.getVersion() == null) { + sce.getElements().add("bundleversion"); + } + + if (connInstanceTO.getConnectorName() == null) { + sce.getElements().add("connectorname"); + } + + if (connInstanceTO.getConfiguration() == null || connInstanceTO.getConfiguration().isEmpty()) { + sce.getElements().add("configuration"); + } + + ConnInstance connInstance = entityFactory.newEntity(ConnInstance.class); + + BeanUtils.copyProperties(connInstanceTO, connInstance, IGNORE_PROPERTIES); + if (connInstanceTO.getLocation() != null) { + connInstance.setLocation(connInstanceTO.getLocation()); + } + if (connInstanceTO.getPoolConf() != null) { + connInstance.setPoolConf( + ConnPoolConfUtil.getConnPoolConf(connInstanceTO.getPoolConf(), entityFactory.newConnPoolConf())); + } + + // Throw exception if there is at least one element set + if (!sce.isEmpty()) { + throw sce; + } + + return connInstance; + } + + @Override + public ConnInstance updateConnInstance(final long connInstanceId, final ConnInstanceTO connInstanceTO) { + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing); + + if (connInstanceId == 0) { + sce.getElements().add("connector id"); + } + + ConnInstance connInstance = connInstanceDAO.find(connInstanceId); + connInstance.getCapabilities().clear(); + connInstance.getCapabilities().addAll(connInstanceTO.getCapabilities()); + + if (connInstanceTO.getLocation() != null) { + connInstance.setLocation(connInstanceTO.getLocation()); + } + + if (connInstanceTO.getBundleName() != null) { + connInstance.setBundleName(connInstanceTO.getBundleName()); + } + + if (connInstanceTO.getVersion() != null) { + connInstance.setVersion(connInstanceTO.getVersion()); + } + + if (connInstanceTO.getConnectorName() != null) { + connInstance.setConnectorName(connInstanceTO.getConnectorName()); + } + + if (connInstanceTO.getConfiguration() != null && !connInstanceTO.getConfiguration().isEmpty()) { + connInstance.setConfiguration(connInstanceTO.getConfiguration()); + } + + if (connInstanceTO.getDisplayName() != null) { + connInstance.setDisplayName(connInstanceTO.getDisplayName()); + } + + if (connInstanceTO.getConnRequestTimeout() != null) { + connInstance.setConnRequestTimeout(connInstanceTO.getConnRequestTimeout()); + } + + if (connInstanceTO.getPoolConf() == null) { + connInstance.setPoolConf(null); + } else { + connInstance.setPoolConf( + ConnPoolConfUtil.getConnPoolConf(connInstanceTO.getPoolConf(), entityFactory.newConnPoolConf())); + } + + if (!sce.isEmpty()) { + throw sce; + } + + return connInstance; + } + + @Override + public ConnConfPropSchema buildConnConfPropSchema(final ConfigurationProperty property) { + ConnConfPropSchema connConfPropSchema = new ConnConfPropSchema(); + + connConfPropSchema.setName(property.getName()); + connConfPropSchema.setDisplayName(property.getDisplayName(property.getName())); + connConfPropSchema.setHelpMessage(property.getHelpMessage(property.getName())); + connConfPropSchema.setRequired(property.isRequired()); + connConfPropSchema.setType(property.getType().getName()); + connConfPropSchema.setOrder(((ConfigurationPropertyImpl) property).getOrder()); + connConfPropSchema.setConfidential(property.isConfidential()); + + if (property.getValue() != null) { + if (property.getValue().getClass().isArray()) { + connConfPropSchema.getDefaultValues().addAll(Arrays.asList((Object[]) property.getValue())); + } else if (property.getValue() instanceof Collection<?>) { + connConfPropSchema.getDefaultValues().addAll((Collection<?>) property.getValue()); + } else { + connConfPropSchema.getDefaultValues().add(property.getValue()); + } + } + + return connConfPropSchema; + } + + @Override + public ConnInstanceTO getConnInstanceTO(final ConnInstance connInstance) { + ConnInstanceTO connInstanceTO = new ConnInstanceTO(); + connInstanceTO.setKey(connInstance.getKey() == null ? 0L : connInstance.getKey()); + + // retrieve the ConfigurationProperties + ConfigurationProperties properties = connIdBundleManager.getConfigurationProperties( + connIdBundleManager.getConnectorInfo(connInstance.getLocation(), + connInstance.getBundleName(), connInstance.getVersion(), connInstance.getConnectorName())); + + BeanUtils.copyProperties(connInstance, connInstanceTO, IGNORE_PROPERTIES); + + final Map<String, ConnConfProperty> connInstanceToConfMap = connInstanceTO.getConfigurationMap(); + + for (String propName : properties.getPropertyNames()) { + ConnConfPropSchema schema = buildConnConfPropSchema(properties.getProperty(propName)); + + ConnConfProperty property; + if (connInstanceToConfMap.containsKey(propName)) { + property = connInstanceToConfMap.get(propName); + } else { + property = new ConnConfProperty(); + connInstanceTO.getConfiguration().add(property); + } + + property.setSchema(schema); + } + + if (connInstance.getPoolConf() != null) { + ConnPoolConfTO poolConf = new ConnPoolConfTO(); + BeanUtils.copyProperties(connInstance.getPoolConf(), poolConf); + connInstanceTO.setPoolConf(poolConf); + } + + return connInstanceTO; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java new file mode 100644 index 0000000..c18e079 --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java @@ -0,0 +1,66 @@ +/* + * 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.data; + +import org.apache.syncope.core.provisioning.api.data.NotificationDataBinder; +import org.apache.syncope.common.lib.to.NotificationTO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.Notification; +import org.apache.syncope.core.misc.spring.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class NotificationDataBinderImpl implements NotificationDataBinder { + + private static final String[] IGNORE_PROPERTIES = { "key", "about", "recipients" }; + + @Autowired + private EntityFactory entityFactory; + + @Override + public NotificationTO getNotificationTO(final Notification notification) { + NotificationTO result = new NotificationTO(); + + BeanUtils.copyProperties(notification, result, IGNORE_PROPERTIES); + + result.setKey(notification.getKey()); + result.setUserAbout(notification.getUserAbout()); + result.setRoleAbout(notification.getRoleAbout()); + result.setRecipients(notification.getRecipients()); + + return result; + } + + @Override + public Notification create(final NotificationTO notificationTO) { + Notification result = entityFactory.newEntity(Notification.class); + update(result, notificationTO); + return result; + } + + @Override + public void update(final Notification notification, final NotificationTO notificationTO) { + BeanUtils.copyProperties(notificationTO, notification, IGNORE_PROPERTIES); + + notification.setUserAbout(notificationTO.getUserAbout()); + notification.setRoleAbout(notificationTO.getRoleAbout()); + notification.setRecipients(notificationTO.getRecipients()); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java new file mode 100644 index 0000000..ec735b6 --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java @@ -0,0 +1,189 @@ +/* + * 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.data; + +import org.apache.syncope.core.provisioning.api.data.PolicyDataBinder; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.AbstractPolicyTO; +import org.apache.syncope.common.lib.to.AccountPolicyTO; +import org.apache.syncope.common.lib.to.PasswordPolicyTO; +import org.apache.syncope.common.lib.to.SyncPolicyTO; +import org.apache.syncope.common.lib.types.AccountPolicySpec; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.PasswordPolicySpec; +import org.apache.syncope.common.lib.types.SyncPolicySpec; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.RoleDAO; +import org.apache.syncope.core.persistence.api.entity.AccountPolicy; +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.PasswordPolicy; +import org.apache.syncope.core.persistence.api.entity.Policy; +import org.apache.syncope.core.persistence.api.entity.SyncPolicy; +import org.apache.syncope.core.persistence.api.entity.role.Role; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class PolicyDataBinderImpl implements PolicyDataBinder { + + /** + * Logger. + */ + private static final Logger LOG = LoggerFactory.getLogger(PolicyDataBinder.class); + + @Autowired + private ExternalResourceDAO resourceDAO; + + @Autowired + private RoleDAO roleDAO; + + @Autowired + private EntityFactory entityFactory; + + /** + * Get policy TO from policy bean. + * + * @param policy bean. + * @return policy TO. + */ + @SuppressWarnings("unchecked") + @Override + public <T extends AbstractPolicyTO> T getPolicyTO(final Policy policy) { + final T policyTO; + + switch (policy.getType()) { + case GLOBAL_PASSWORD: + case PASSWORD: + policyTO = (T) new PasswordPolicyTO(policy.getType().isGlobal()); + ((PasswordPolicyTO) policyTO).setSpecification(policy.getSpecification(PasswordPolicySpec.class)); + break; + + case GLOBAL_ACCOUNT: + case ACCOUNT: + policyTO = (T) new AccountPolicyTO(policy.getType().isGlobal()); + ((AccountPolicyTO) policyTO).setSpecification(policy.getSpecification(AccountPolicySpec.class)); + ((AccountPolicyTO) policyTO).getResources().addAll(((AccountPolicy) policy).getResourceNames()); + break; + + case GLOBAL_SYNC: + case SYNC: + default: + policyTO = (T) new SyncPolicyTO(policy.getType().isGlobal()); + ((SyncPolicyTO) policyTO).setSpecification(policy.getSpecification(SyncPolicySpec.class)); + } + + policyTO.setKey(policy.getKey()); + policyTO.setDescription(policy.getDescription()); + + for (ExternalResource resource : resourceDAO.findByPolicy(policy)) { + policyTO.getUsedByResources().add(resource.getKey()); + } + if (policy.getType().isGlobal()) { + for (ExternalResource resource : resourceDAO.findWithoutPolicy(policy.getType())) { + policyTO.getUsedByResources().add(resource.getKey()); + } + } + for (Role role : roleDAO.findByPolicy(policy)) { + policyTO.getUsedByRoles().add(role.getKey()); + } + if (policy.getType().isGlobal()) { + for (Role role : roleDAO.findWithoutPolicy(policy.getType())) { + policyTO.getUsedByRoles().add(role.getKey()); + } + } + + return policyTO; + } + + private ExternalResource getResource(final String resourceName) { + ExternalResource resource = resourceDAO.find(resourceName); + if (resource == null) { + LOG.debug("Ignoring invalid resource {} ", resourceName); + } + + return resource; + } + + @SuppressWarnings("unchecked") + @Override + public <T extends Policy> T getPolicy(T policy, final AbstractPolicyTO policyTO) { + if (policy != null && policy.getType() != policyTO.getType()) { + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy); + sce.getElements().add(String.format("Cannot update %s from %s", policy.getType(), policyTO.getType())); + throw sce; + } + + switch (policyTO.getType()) { + case GLOBAL_PASSWORD: + case PASSWORD: + if (!(policyTO instanceof PasswordPolicyTO)) { + throw new ClassCastException("Expected " + PasswordPolicyTO.class.getName() + + ", found " + policyTO.getClass().getName()); + } + if (policy == null) { + policy = (T) entityFactory.newPolicy(PasswordPolicy.class, policyTO.getType().isGlobal()); + } + policy.setSpecification(((PasswordPolicyTO) policyTO).getSpecification()); + break; + + case GLOBAL_ACCOUNT: + case ACCOUNT: + if (!(policyTO instanceof AccountPolicyTO)) { + throw new ClassCastException("Expected " + AccountPolicyTO.class.getName() + + ", found " + policyTO.getClass().getName()); + } + if (policy == null) { + policy = (T) entityFactory.newPolicy(AccountPolicy.class, policyTO.getType().isGlobal()); + } + policy.setSpecification(((AccountPolicyTO) policyTO).getSpecification()); + + if (((AccountPolicy) policy).getResources() != null + && !((AccountPolicy) policy).getResources().isEmpty()) { + ((AccountPolicy) policy).getResources().clear(); + } + for (String resourceName : ((AccountPolicyTO) policyTO).getResources()) { + ExternalResource resource = getResource(resourceName); + + if (resource != null) { + ((AccountPolicy) policy).addResource(resource); + } + } + break; + + case GLOBAL_SYNC: + case SYNC: + default: + if (!(policyTO instanceof SyncPolicyTO)) { + throw new ClassCastException("Expected " + SyncPolicyTO.class.getName() + + ", found " + policyTO.getClass().getName()); + } + if (policy == null) { + policy = (T) entityFactory.newPolicy(SyncPolicy.class, policyTO.getType().isGlobal()); + } + policy.setSpecification(((SyncPolicyTO) policyTO).getSpecification()); + } + + policy.setDescription(policyTO.getDescription()); + + return policy; + } +}
