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/ReportDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ReportDataBinderImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ReportDataBinderImpl.java new file mode 100644 index 0000000..e98bc7d --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ReportDataBinderImpl.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.provisioning.java.data; + +import java.util.HashSet; +import java.util.Set; +import org.apache.syncope.core.provisioning.api.data.ReportDataBinder; +import org.apache.syncope.common.lib.report.AbstractReportletConf; +import org.apache.syncope.common.lib.report.ReportletConf; +import org.apache.syncope.common.lib.to.ReportExecTO; +import org.apache.syncope.common.lib.to.ReportTO; +import org.apache.syncope.core.persistence.api.dao.ReportExecDAO; +import org.apache.syncope.core.persistence.api.entity.Report; +import org.apache.syncope.core.persistence.api.entity.ReportExec; +import org.apache.syncope.core.provisioning.api.job.JobNamer; +import org.apache.syncope.core.misc.spring.BeanUtils; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.Trigger; +import org.quartz.TriggerKey; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; +import org.springframework.stereotype.Component; + +@Component +public class ReportDataBinderImpl implements ReportDataBinder { + + /** + * Logger. + */ + private static final Logger LOG = LoggerFactory.getLogger(ReportDataBinder.class); + + private static final String[] IGNORE_REPORT_PROPERTIES = { "key", "reportlets", "executions" }; + + private static final String[] IGNORE_REPORT_EXECUTION_PROPERTIES = { "key", "report", "execResult" }; + + @Autowired + private ReportExecDAO reportExecDAO; + + @Autowired + private SchedulerFactoryBean scheduler; + + @Override + public void getReport(final Report report, final ReportTO reportTO) { + BeanUtils.copyProperties(reportTO, report, IGNORE_REPORT_PROPERTIES); + + // 1. remove all reportlet confs + Set<ReportletConf> toRemove = new HashSet<>(); + for (ReportletConf conf : report.getReportletConfs()) { + toRemove.add(conf); + } + for (ReportletConf conf : toRemove) { + report.removeReportletConf(conf); + } + + // 2. take all reportlet confs from reportTO + for (ReportletConf conf : reportTO.getReportletConfs()) { + report.addReportletConf(conf); + } + } + + @Override + public ReportTO getReportTO(final Report report) { + ReportTO reportTO = new ReportTO(); + reportTO.setKey(report.getKey()); + BeanUtils.copyProperties(report, reportTO, IGNORE_REPORT_PROPERTIES); + + copyReportletConfs(report, reportTO); + + ReportExec latestExec = reportExecDAO.findLatestStarted(report); + reportTO.setLatestExecStatus(latestExec == null + ? "" + : latestExec.getStatus()); + + reportTO.setStartDate(latestExec == null + ? null + : latestExec.getStartDate()); + + reportTO.setEndDate(latestExec == null + ? null + : latestExec.getEndDate()); + + for (ReportExec reportExec : report.getExecs()) { + reportTO.getExecutions().add(getReportExecTO(reportExec)); + } + + String triggerName = JobNamer.getTriggerName(JobNamer.getJobName(report)); + + Trigger trigger; + try { + trigger = scheduler.getScheduler().getTrigger(new TriggerKey(triggerName, Scheduler.DEFAULT_GROUP)); + } catch (SchedulerException e) { + LOG.warn("While trying to get to " + triggerName, e); + trigger = null; + } + + if (trigger != null) { + reportTO.setLastExec(trigger.getPreviousFireTime()); + reportTO.setNextExec(trigger.getNextFireTime()); + } + + return reportTO; + } + + private void copyReportletConfs(final Report report, final ReportTO reportTO) { + reportTO.getReportletConfs().clear(); + for (ReportletConf reportletConf : report.getReportletConfs()) { + reportTO.getReportletConfs().add((AbstractReportletConf) reportletConf); + } + } + + @Override + public ReportExecTO getReportExecTO(final ReportExec execution) { + ReportExecTO executionTO = new ReportExecTO(); + executionTO.setKey(execution.getKey()); + BeanUtils.copyProperties(execution, executionTO, IGNORE_REPORT_EXECUTION_PROPERTIES); + if (execution.getKey() != null) { + executionTO.setKey(execution.getKey()); + } + executionTO.setReport(execution.getReport().getKey()); + + return executionTO; + } +}
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/ResourceDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java new file mode 100644 index 0000000..2bf756f --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java @@ -0,0 +1,362 @@ +/* + * 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.ResourceDataBinder; +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.SerializationUtils; +import org.apache.syncope.common.lib.SyncopeClientCompositeException; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.MappingItemTO; +import org.apache.syncope.common.lib.to.MappingTO; +import org.apache.syncope.common.lib.to.ResourceTO; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.IntMappingType; +import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.PolicyDAO; +import org.apache.syncope.core.persistence.api.entity.AccountPolicy; +import org.apache.syncope.core.persistence.api.entity.ConnInstance; +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.Mapping; +import org.apache.syncope.core.persistence.api.entity.MappingItem; +import org.apache.syncope.core.persistence.api.entity.PasswordPolicy; +import org.apache.syncope.core.persistence.api.entity.SyncPolicy; +import org.apache.syncope.core.persistence.api.entity.role.RMapping; +import org.apache.syncope.core.persistence.api.entity.role.RMappingItem; +import org.apache.syncope.core.persistence.api.entity.user.UMapping; +import org.apache.syncope.core.persistence.api.entity.user.UMappingItem; +import org.apache.syncope.core.provisioning.api.ConnectorRegistry; +import org.apache.syncope.core.misc.jexl.JexlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.syncope.core.misc.spring.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ResourceDataBinderImpl implements ResourceDataBinder { + + /** + * Logger. + */ + private static final Logger LOG = LoggerFactory.getLogger(ResourceDataBinder.class); + + private static final String[] MAPPINGITEM_IGNORE_PROPERTIES = { "key", "mapping" }; + + @Autowired + private ConnectorRegistry connRegistry; + + @Autowired + private ConnInstanceDAO connInstanceDAO; + + @Autowired + private PolicyDAO policyDAO; + + @Autowired + private EntityFactory entityFactory; + + @Override + public ExternalResource create(final ResourceTO resourceTO) { + return update(entityFactory.newEntity(ExternalResource.class), resourceTO); + } + + @Override + public ExternalResource update(final ExternalResource resource, final ResourceTO resourceTO) { + if (resourceTO == null) { + return null; + } + + resource.setKey(resourceTO.getKey()); + + if (resourceTO.getConnectorId() != null) { + ConnInstance connector = connInstanceDAO.find(resourceTO.getConnectorId()); + resource.setConnector(connector); + + if (!connector.getResources().contains(resource)) { + connector.addResource(resource); + } + } + + resource.setEnforceMandatoryCondition(resourceTO.isEnforceMandatoryCondition()); + + resource.setPropagationPrimary(resourceTO.isPropagationPrimary()); + + resource.setPropagationPriority(resourceTO.getPropagationPriority()); + + resource.setRandomPwdIfNotProvided(resourceTO.isRandomPwdIfNotProvided()); + + resource.setPropagationMode(resourceTO.getPropagationMode()); + + if (resourceTO.getUmapping() == null || resourceTO.getUmapping().getItems().isEmpty()) { + resource.setUmapping(null); + } else { + UMapping mapping = entityFactory.newEntity(UMapping.class); + mapping.setResource(resource); + resource.setUmapping(mapping); + populateMapping(resourceTO.getUmapping(), mapping, entityFactory.newEntity(UMappingItem.class)); + } + if (resourceTO.getRmapping() == null || resourceTO.getRmapping().getItems().isEmpty()) { + resource.setRmapping(null); + } else { + RMapping mapping = entityFactory.newEntity(RMapping.class); + mapping.setResource(resource); + resource.setRmapping(mapping); + populateMapping(resourceTO.getRmapping(), mapping, entityFactory.newEntity(RMappingItem.class)); + } + + resource.setCreateTraceLevel(resourceTO.getCreateTraceLevel()); + resource.setUpdateTraceLevel(resourceTO.getUpdateTraceLevel()); + resource.setDeleteTraceLevel(resourceTO.getDeleteTraceLevel()); + resource.setSyncTraceLevel(resourceTO.getSyncTraceLevel()); + + resource.setPasswordPolicy(resourceTO.getPasswordPolicy() == null + ? null : (PasswordPolicy) policyDAO.find(resourceTO.getPasswordPolicy())); + + resource.setAccountPolicy(resourceTO.getAccountPolicy() == null + ? null : (AccountPolicy) policyDAO.find(resourceTO.getAccountPolicy())); + + resource.setSyncPolicy(resourceTO.getSyncPolicy() == null + ? null : (SyncPolicy) policyDAO.find(resourceTO.getSyncPolicy())); + + resource.setConnInstanceConfiguration(new HashSet<>(resourceTO.getConnConfProperties())); + + if (resourceTO.getUsyncToken() == null) { + resource.setUsyncToken(null); + } + if (resourceTO.getRsyncToken() == null) { + resource.setRsyncToken(null); + } + + resource.getPropagationActionsClassNames().clear(); + resource.getPropagationActionsClassNames().addAll(resourceTO.getPropagationActionsClassNames()); + + return resource; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void populateMapping(final MappingTO mappingTO, final Mapping mapping, final MappingItem prototype) { + mapping.setAccountLink(mappingTO.getAccountLink()); + + for (MappingItem item : getMappingItems(mappingTO.getItems(), prototype)) { + item.setMapping(mapping); + if (item.isAccountid()) { + mapping.setAccountIdItem(item); + } else if (item.isPassword()) { + ((UMapping) mapping).setPasswordItem((UMappingItem) item); + } else { + mapping.addItem(item); + } + } + } + + private Set<MappingItem> getMappingItems(final Collection<MappingItemTO> itemTOs, final MappingItem prototype) { + Set<MappingItem> items = new HashSet<>(itemTOs.size()); + for (MappingItemTO itemTO : itemTOs) { + items.add(getMappingItem(itemTO, prototype)); + } + + return items; + } + + private MappingItem getMappingItem(final MappingItemTO itemTO, final MappingItem prototype) { + if (itemTO == null || itemTO.getIntMappingType() == null) { + LOG.error("Null mappingTO provided"); + return null; + } + + SyncopeClientCompositeException scce = SyncopeClientException.buildComposite(); + + SyncopeClientException requiredValuesMissing = SyncopeClientException.build( + ClientExceptionType.RequiredValuesMissing); + + if (itemTO.getIntAttrName() == null) { + if (IntMappingType.getEmbedded().contains(itemTO.getIntMappingType())) { + itemTO.setIntAttrName(itemTO.getIntMappingType().toString()); + } else { + requiredValuesMissing.getElements().add("intAttrName"); + } + } + + // Throw composite exception if there is at least one element set + // in the composing exceptions + if (!requiredValuesMissing.isEmpty()) { + scce.addException(requiredValuesMissing); + } + + // no mandatory condition implies mandatory condition false + if (!JexlUtil.isExpressionValid(itemTO.getMandatoryCondition() == null + ? "false" : itemTO.getMandatoryCondition())) { + + SyncopeClientException invalidMandatoryCondition = SyncopeClientException.build( + ClientExceptionType.InvalidValues); + + invalidMandatoryCondition.getElements().add(itemTO.getMandatoryCondition()); + + scce.addException(invalidMandatoryCondition); + } + + if (scce.hasExceptions()) { + throw scce; + } + + MappingItem item = SerializationUtils.clone(prototype); + BeanUtils.copyProperties(itemTO, item, MAPPINGITEM_IGNORE_PROPERTIES); + return item; + } + + @Override + public ConnInstance getConnInstance(final ResourceTO resourceTO) { + ConnInstance connInstance = connInstanceDAO.find(resourceTO.getConnectorId()); + if (connInstance == null) { + throw new NotFoundException("Connector '" + resourceTO.getConnectorId() + "'"); + } + + final ConnInstance connInstanceClone = SerializationUtils.clone(connInstance); + return connRegistry.getOverriddenConnInstance(connInstanceClone, resourceTO.getConnConfProperties()); + } + + @Override + public List<ResourceTO> getResourceTOs(final Collection<? extends ExternalResource> resources) { + List<ResourceTO> resourceTOs = new ArrayList<>(); + for (ExternalResource resource : resources) { + resourceTOs.add(getResourceTO(resource)); + } + + return resourceTOs; + } + + @Override + public ResourceTO getResourceTO(final ExternalResource resource) { + if (resource == null) { + return null; + } + + ResourceTO resourceTO = new ResourceTO(); + + // set sys info + resourceTO.setCreator(resource.getCreator()); + resourceTO.setCreationDate(resource.getCreationDate()); + resourceTO.setLastModifier(resource.getLastModifier()); + resourceTO.setLastChangeDate(resource.getLastChangeDate()); + + // set the resource name + resourceTO.setKey(resource.getKey()); + + // set the connector instance + ConnInstance connector = resource.getConnector(); + + resourceTO.setConnectorId(connector == null ? null : connector.getKey()); + resourceTO.setConnectorDisplayName(connector == null ? null : connector.getDisplayName()); + + // set the mappings + if (resource.getUmapping() != null) { + MappingTO mappingTO = new MappingTO(); + resourceTO.setUmapping(mappingTO); + populateMappingTO(resource.getUmapping(), mappingTO); + } + if (resource.getRmapping() != null) { + MappingTO mappingTO = new MappingTO(); + resourceTO.setRmapping(mappingTO); + populateMappingTO(resource.getRmapping(), mappingTO); + } + + resourceTO.setEnforceMandatoryCondition(resource.isEnforceMandatoryCondition()); + + resourceTO.setPropagationPrimary(resource.isPropagationPrimary()); + + resourceTO.setPropagationPriority(resource.getPropagationPriority()); + + resourceTO.setRandomPwdIfNotProvided(resource.isRandomPwdIfNotProvided()); + + resourceTO.setPropagationMode(resource.getPropagationMode()); + + resourceTO.setCreateTraceLevel(resource.getCreateTraceLevel()); + resourceTO.setUpdateTraceLevel(resource.getUpdateTraceLevel()); + resourceTO.setDeleteTraceLevel(resource.getDeleteTraceLevel()); + resourceTO.setSyncTraceLevel(resource.getSyncTraceLevel()); + + resourceTO.setPasswordPolicy(resource.getPasswordPolicy() == null + ? null : resource.getPasswordPolicy().getKey()); + + resourceTO.setAccountPolicy(resource.getAccountPolicy() == null + ? null : resource.getAccountPolicy().getKey()); + + resourceTO.setSyncPolicy(resource.getSyncPolicy() == null + ? null : resource.getSyncPolicy().getKey()); + + resourceTO.getConnConfProperties().addAll(resource.getConnInstanceConfiguration()); + + resourceTO.setUsyncToken(resource.getSerializedUSyncToken()); + resourceTO.setRsyncToken(resource.getSerializedRSyncToken()); + + resourceTO.getPropagationActionsClassNames().addAll(resource.getPropagationActionsClassNames()); + + return resourceTO; + } + + private void populateMappingTO(final Mapping<?> mapping, final MappingTO mappingTO) { + mappingTO.setAccountLink(mapping.getAccountLink()); + + for (MappingItemTO itemTO : getMappingItemTOs(mapping.getItems())) { + if (itemTO.isAccountid()) { + mappingTO.setAccountIdItem(itemTO); + } else if (itemTO.isPassword()) { + mappingTO.setPasswordItem(itemTO); + } else { + mappingTO.addItem(itemTO); + } + } + } + + private Set<MappingItemTO> getMappingItemTOs(final Collection<? extends MappingItem> items) { + Set<MappingItemTO> mappingTOs = new HashSet<>(); + for (MappingItem item : items) { + LOG.debug("Asking for TO for {}", item); + mappingTOs.add(getMappingItemTO(item)); + } + + LOG.debug("Collected TOs: {}", mappingTOs); + + return mappingTOs; + } + + private MappingItemTO getMappingItemTO(final MappingItem item) { + if (item == null) { + LOG.error("Provided null mapping"); + + return null; + } + + MappingItemTO itemTO = new MappingItemTO(); + + BeanUtils.copyProperties(item, itemTO, MAPPINGITEM_IGNORE_PROPERTIES); + + itemTO.setKey(item.getKey()); + + LOG.debug("Obtained SchemaMappingTO {}", itemTO); + + return itemTO; + } +} 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/RoleDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java new file mode 100644 index 0000000..2e12820 --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java @@ -0,0 +1,411 @@ +/* + * 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.ArrayList; +import java.util.List; +import java.util.Map; +import org.apache.syncope.common.lib.SyncopeClientCompositeException; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.mod.RoleMod; +import org.apache.syncope.common.lib.to.RoleTO; +import org.apache.syncope.common.lib.types.AttributableType; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.core.persistence.api.dao.EntitlementDAO; +import org.apache.syncope.core.persistence.api.entity.AccountPolicy; +import org.apache.syncope.core.persistence.api.entity.AttrTemplate; +import org.apache.syncope.core.persistence.api.entity.Entitlement; +import org.apache.syncope.core.persistence.api.entity.PasswordPolicy; +import org.apache.syncope.core.persistence.api.entity.Schema; +import org.apache.syncope.core.persistence.api.entity.membership.MDerAttrTemplate; +import org.apache.syncope.core.persistence.api.entity.membership.MDerSchema; +import org.apache.syncope.core.persistence.api.entity.membership.MPlainAttrTemplate; +import org.apache.syncope.core.persistence.api.entity.membership.MPlainSchema; +import org.apache.syncope.core.persistence.api.entity.membership.MVirAttrTemplate; +import org.apache.syncope.core.persistence.api.entity.membership.MVirSchema; +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.RDerSchema; +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.RPlainSchema; +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.RVirSchema; +import org.apache.syncope.core.persistence.api.entity.role.Role; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.common.lib.types.PropagationByResource; +import org.apache.syncope.core.provisioning.api.data.RoleDataBinder; +import org.apache.syncope.core.misc.ConnObjectUtil; +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 RoleDataBinderImpl extends AbstractAttributableDataBinder implements RoleDataBinder { + + @Autowired + private ConnObjectUtil connObjectUtil; + + @Autowired + private EntitlementDAO entitlementDAO; + + private <T extends AttrTemplate<S>, S extends Schema> void setAttrTemplates( + final Role role, final List<String> schemaNames, + final Class<T> templateClass, final Class<S> schemaClass) { + + List<T> toRemove = new ArrayList<>(); + for (T template : role.getAttrTemplates(templateClass)) { + if (!schemaNames.contains(template.getSchema().getKey())) { + toRemove.add(template); + } + } + role.getAttrTemplates(templateClass).removeAll(toRemove); + + for (String schemaName : schemaNames) { + if (role.getAttrTemplate(templateClass, schemaName) == null) { + S schema = getSchema(schemaName, schemaClass); + if (schema != null) { + try { + T template = entityFactory.newEntity(templateClass); + template.setSchema(schema); + template.setOwner(role); + role.getAttrTemplates(templateClass).add(template); + } catch (Exception e) { + LOG.error("Could not create template for {}", templateClass, e); + } + } + } + } + } + + @Override + public Role create(final Role role, final RoleTO roleTO) { + role.setInheritOwner(roleTO.isInheritOwner()); + + role.setInheritPlainAttrs(roleTO.isInheritPlainAttrs()); + role.setInheritDerAttrs(roleTO.isInheritDerAttrs()); + role.setInheritVirAttrs(roleTO.isInheritVirAttrs()); + + role.setInheritTemplates(roleTO.isInheritTemplates()); + + role.setInheritPasswordPolicy(roleTO.isInheritPasswordPolicy()); + role.setInheritAccountPolicy(roleTO.isInheritAccountPolicy()); + + SyncopeClientCompositeException scce = SyncopeClientException.buildComposite(); + + // name and parent + SyncopeClientException invalidRoles = SyncopeClientException.build(ClientExceptionType.InvalidRoles); + if (roleTO.getName() == null) { + LOG.error("No name specified for this role"); + + invalidRoles.getElements().add("No name specified for this role"); + } else { + role.setName(roleTO.getName()); + } + Long parentRoleKey = null; + if (roleTO.getParent() != 0) { + Role parentRole = roleDAO.find(roleTO.getParent()); + if (parentRole == null) { + LOG.error("Could not find role with id " + roleTO.getParent()); + + invalidRoles.getElements().add(String.valueOf(roleTO.getParent())); + scce.addException(invalidRoles); + } else { + role.setParent(parentRole); + parentRoleKey = role.getParent().getKey(); + } + } + + Role otherRole = roleDAO.find(roleTO.getName(), parentRoleKey); + if (otherRole != null) { + LOG.error("Another role exists with the same name " + "and the same parent role: " + otherRole); + + invalidRoles.getElements().add(roleTO.getName()); + } + + // attribute templates + setAttrTemplates(role, roleTO.getRPlainAttrTemplates(), RPlainAttrTemplate.class, RPlainSchema.class); + setAttrTemplates(role, roleTO.getRDerAttrTemplates(), RDerAttrTemplate.class, RDerSchema.class); + setAttrTemplates(role, roleTO.getRVirAttrTemplates(), RVirAttrTemplate.class, RVirSchema.class); + setAttrTemplates(role, roleTO.getMPlainAttrTemplates(), MPlainAttrTemplate.class, MPlainSchema.class); + setAttrTemplates(role, roleTO.getMDerAttrTemplates(), MDerAttrTemplate.class, MDerSchema.class); + setAttrTemplates(role, roleTO.getMVirAttrTemplates(), MVirAttrTemplate.class, MVirSchema.class); + + // attributes, derived attributes, virtual attributes and resources + fill(role, roleTO, attrUtilFactory.getInstance(AttributableType.ROLE), scce); + + // entitlements + for (String entitlementName : roleTO.getEntitlements()) { + Entitlement entitlement = entitlementDAO.find(entitlementName); + if (entitlement == null) { + LOG.warn("Ignoring invalid entitlement {}", entitlementName); + } else { + role.addEntitlement(entitlement); + } + } + + // owner + if (roleTO.getUserOwner() != null) { + User owner = userDAO.find(roleTO.getUserOwner()); + if (owner == null) { + LOG.warn("Ignoring invalid user specified as owner: {}", roleTO.getUserOwner()); + } else { + role.setUserOwner(owner); + } + } + if (roleTO.getRoleOwner() != null) { + Role owner = roleDAO.find(roleTO.getRoleOwner()); + if (owner == null) { + LOG.warn("Ignoring invalid role specified as owner: {}", roleTO.getRoleOwner()); + } else { + role.setRoleOwner(owner); + } + } + + // policies + if (roleTO.getPasswordPolicy() != null) { + role.setPasswordPolicy((PasswordPolicy) policyDAO.find(roleTO.getPasswordPolicy())); + } + if (roleTO.getAccountPolicy() != null) { + role.setAccountPolicy((AccountPolicy) policyDAO.find(roleTO.getAccountPolicy())); + } + + return role; + } + + @Override + public PropagationByResource update(final Role role, final RoleMod roleMod) { + PropagationByResource propByRes = new PropagationByResource(); + + SyncopeClientCompositeException scce = SyncopeClientException.buildComposite(); + + // fetch account ids before update + Map<String, String> oldAccountIds = getAccountIds(role, AttributableType.ROLE); + + // name + SyncopeClientException invalidRoles = SyncopeClientException.build(ClientExceptionType.InvalidRoles); + if (roleMod.getName() != null) { + Role otherRole = roleDAO.find(roleMod.getName(), + role.getParent() == null ? null : role.getParent().getKey()); + if (otherRole == null || role.equals(otherRole)) { + if (!roleMod.getName().equals(role.getName())) { + propByRes.addAll(ResourceOperation.UPDATE, role.getResourceNames()); + + role.setName(roleMod.getName()); + } + } else { + LOG.error("Another role exists with the same name and the same parent role: " + otherRole); + + invalidRoles.getElements().add(roleMod.getName()); + scce.addException(invalidRoles); + } + } + + if (roleMod.getInheritOwner() != null) { + role.setInheritOwner(roleMod.getInheritOwner()); + } + + if (roleMod.getInheritTemplates() != null) { + role.setInheritTemplates(roleMod.getInheritTemplates()); + } + + if (roleMod.getInheritPlainAttrs() != null) { + role.setInheritPlainAttrs(roleMod.getInheritPlainAttrs()); + } + if (roleMod.getInheritDerAttrs() != null) { + role.setInheritDerAttrs(roleMod.getInheritDerAttrs()); + } + if (roleMod.getInheritVirAttrs() != null) { + role.setInheritVirAttrs(roleMod.getInheritVirAttrs()); + } + + if (roleMod.getInheritPasswordPolicy() != null) { + role.setInheritPasswordPolicy(roleMod.getInheritPasswordPolicy()); + } + if (roleMod.getInheritAccountPolicy() != null) { + role.setInheritAccountPolicy(roleMod.getInheritAccountPolicy()); + } + + // entitlements + if (roleMod.isModEntitlements()) { + role.getEntitlements().clear(); + for (String entitlementName : roleMod.getEntitlements()) { + Entitlement entitlement = entitlementDAO.find(entitlementName); + if (entitlement == null) { + LOG.warn("Ignoring invalid entitlement {}", entitlementName); + } else { + role.addEntitlement(entitlement); + } + } + } + + // attribute templates + if (roleMod.isModRAttrTemplates()) { + setAttrTemplates(role, roleMod.getRPlainAttrTemplates(), RPlainAttrTemplate.class, RPlainSchema.class); + } + if (roleMod.isModRDerAttrTemplates()) { + setAttrTemplates(role, roleMod.getRDerAttrTemplates(), RDerAttrTemplate.class, RDerSchema.class); + } + if (roleMod.isModRVirAttrTemplates()) { + setAttrTemplates(role, roleMod.getRVirAttrTemplates(), RVirAttrTemplate.class, RVirSchema.class); + } + if (roleMod.isModMAttrTemplates()) { + setAttrTemplates(role, roleMod.getMPlainAttrTemplates(), MPlainAttrTemplate.class, MPlainSchema.class); + } + if (roleMod.isModMDerAttrTemplates()) { + setAttrTemplates(role, roleMod.getMDerAttrTemplates(), MDerAttrTemplate.class, MDerSchema.class); + } + if (roleMod.isModMVirAttrTemplates()) { + setAttrTemplates(role, roleMod.getMVirAttrTemplates(), MVirAttrTemplate.class, MVirSchema.class); + } + + // policies + if (roleMod.getPasswordPolicy() != null) { + role.setPasswordPolicy(roleMod.getPasswordPolicy().getKey() == null + ? null + : (PasswordPolicy) policyDAO.find(roleMod.getPasswordPolicy().getKey())); + } + if (roleMod.getAccountPolicy() != null) { + role.setAccountPolicy(roleMod.getAccountPolicy().getKey() == null + ? null + : (AccountPolicy) policyDAO.find(roleMod.getAccountPolicy().getKey())); + } + + // owner + if (roleMod.getUserOwner() != null) { + role.setUserOwner(roleMod.getUserOwner().getKey() == null + ? null + : userDAO.find(roleMod.getUserOwner().getKey())); + } + if (roleMod.getRoleOwner() != null) { + role.setRoleOwner(roleMod.getRoleOwner().getKey() == null + ? null + : roleDAO.find(roleMod.getRoleOwner().getKey())); + } + + // attributes, derived attributes, virtual attributes and resources + propByRes.merge(fill(role, roleMod, attrUtilFactory.getInstance(AttributableType.ROLE), scce)); + + // check if some account id was changed by the update above + Map<String, String> newAccountIds = getAccountIds(role, AttributableType.ROLE); + for (Map.Entry<String, String> entry : oldAccountIds.entrySet()) { + if (newAccountIds.containsKey(entry.getKey()) + && !entry.getValue().equals(newAccountIds.get(entry.getKey()))) { + + propByRes.addOldAccountId(entry.getKey(), entry.getValue()); + propByRes.add(ResourceOperation.UPDATE, entry.getKey()); + } + } + + return propByRes; + } + + @SuppressWarnings("unchecked") + @Transactional(readOnly = true) + @Override + public RoleTO getRoleTO(final Role role) { + connObjectUtil.retrieveVirAttrValues(role, attrUtilFactory.getInstance(AttributableType.ROLE)); + + RoleTO roleTO = new RoleTO(); + + // set sys info + roleTO.setCreator(role.getCreator()); + roleTO.setCreationDate(role.getCreationDate()); + roleTO.setLastModifier(role.getLastModifier()); + roleTO.setLastChangeDate(role.getLastChangeDate()); + + roleTO.setKey(role.getKey()); + roleTO.setName(role.getName()); + + roleTO.setInheritOwner(role.isInheritOwner()); + + roleTO.setInheritTemplates(role.isInheritTemplates()); + + roleTO.setInheritPlainAttrs(role.isInheritPlainAttrs()); + roleTO.setInheritDerAttrs(role.isInheritDerAttrs()); + roleTO.setInheritVirAttrs(role.isInheritVirAttrs()); + + roleTO.setInheritPasswordPolicy(role.isInheritPasswordPolicy()); + roleTO.setInheritAccountPolicy(role.isInheritAccountPolicy()); + + if (role.getParent() != null) { + roleTO.setParent(role.getParent().getKey()); + } + + if (role.getUserOwner() != null) { + roleTO.setUserOwner(role.getUserOwner().getKey()); + } + if (role.getRoleOwner() != null) { + roleTO.setRoleOwner(role.getRoleOwner().getKey()); + } + + // ------------------------- + // Retrieve all [derived/virtual] attributes (inherited and not) + // ------------------------- + final List<? extends RPlainAttr> allAttributes = role.findLastInheritedAncestorPlainAttrs(); + + final List<? extends RDerAttr> allDerAttributes = role.findLastInheritedAncestorDerAttrs(); + + final List<? extends RVirAttr> allVirAttributes = role.findLastInheritedAncestorVirAttrs(); + // ------------------------- + + fillTO(roleTO, allAttributes, allDerAttributes, allVirAttributes, role.getResources()); + + for (Entitlement entitlement : role.getEntitlements()) { + roleTO.getEntitlements().add(entitlement.getKey()); + } + + for (RPlainAttrTemplate template : role.findInheritedTemplates(RPlainAttrTemplate.class)) { + roleTO.getRPlainAttrTemplates().add(template.getSchema().getKey()); + } + for (RDerAttrTemplate template : role.findInheritedTemplates(RDerAttrTemplate.class)) { + roleTO.getRDerAttrTemplates().add(template.getSchema().getKey()); + } + for (RVirAttrTemplate template : role.findInheritedTemplates(RVirAttrTemplate.class)) { + roleTO.getRVirAttrTemplates().add(template.getSchema().getKey()); + } + for (MPlainAttrTemplate template : role.findInheritedTemplates(MPlainAttrTemplate.class)) { + roleTO.getMPlainAttrTemplates().add(template.getSchema().getKey()); + } + for (MDerAttrTemplate template : role.findInheritedTemplates(MDerAttrTemplate.class)) { + roleTO.getMDerAttrTemplates().add(template.getSchema().getKey()); + } + for (MVirAttrTemplate template : role.findInheritedTemplates(MVirAttrTemplate.class)) { + roleTO.getMVirAttrTemplates().add(template.getSchema().getKey()); + } + + roleTO.setPasswordPolicy(role.getPasswordPolicy() == null + ? null + : role.getPasswordPolicy().getKey()); + roleTO.setAccountPolicy(role.getAccountPolicy() == null + ? null + : role.getAccountPolicy().getKey()); + + return roleTO; + } + + @Transactional(readOnly = true) + @Override + public RoleTO getRoleTO(final Long key) { + return getRoleTO(roleDAO.authFetch(key)); + } +} 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/SchemaDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderImpl.java new file mode 100644 index 0000000..c0adde2 --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderImpl.java @@ -0,0 +1,170 @@ +/* + * 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.SchemaDataBinder; +import java.util.List; +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.to.DerSchemaTO; +import org.apache.syncope.common.lib.to.PlainSchemaTO; +import org.apache.syncope.common.lib.to.VirSchemaTO; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.entity.AttributableUtil; +import org.apache.syncope.core.persistence.api.entity.DerSchema; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.VirSchema; +import org.apache.syncope.core.misc.spring.BeanUtils; +import org.apache.syncope.core.misc.jexl.JexlUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SchemaDataBinderImpl implements SchemaDataBinder { + + @Autowired + private PlainSchemaDAO schemaDAO; + + // --------------- PLAIN ----------------- + private <T extends PlainSchema> void fill(final T schema, final PlainSchemaTO schemaTO) { + if (!JexlUtil.isExpressionValid(schemaTO.getMandatoryCondition())) { + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidValues); + sce.getElements().add(schemaTO.getMandatoryCondition()); + throw sce; + } + + BeanUtils.copyProperties(schemaTO, schema); + } + + @Override + public <T extends PlainSchema> void create(final PlainSchemaTO schemaTO, final T schema) { + fill(schema, schemaTO); + } + + @Override + public <T extends PlainSchema> void update(final PlainSchemaTO schemaTO, final T schema, + final AttributableUtil attributableUtil) { + + SyncopeClientCompositeException scce = SyncopeClientException.buildComposite(); + + List<PlainAttr> attrs = schemaDAO.findAttrs(schema, attributableUtil.plainAttrClass()); + if (!attrs.isEmpty()) { + if (schema.getType() != schemaTO.getType()) { + SyncopeClientException e = SyncopeClientException.build(ClientExceptionType.InvalidPlainSchema); + e.getElements().add("Cannot change type since " + schema.getKey() + " has attributes"); + + scce.addException(e); + } + if (schema.isUniqueConstraint() != schemaTO.isUniqueConstraint()) { + SyncopeClientException e = SyncopeClientException.build(ClientExceptionType.InvalidPlainSchema); + e.getElements().add("Cannot alter unique contraint since " + schema.getKey() + " has attributes"); + + scce.addException(e); + } + } + + if (scce.hasExceptions()) { + throw scce; + } + + fill(schema, schemaTO); + } + + @Override + public <T extends PlainSchema> PlainSchemaTO getSchemaTO( + final T schema, final AttributableUtil attributableUtil) { + + PlainSchemaTO schemaTO = new PlainSchemaTO(); + BeanUtils.copyProperties(schema, schemaTO); + + return schemaTO; + } + + // --------------- DERIVED ----------------- + private <T extends DerSchema> T populate(final T derSchema, final DerSchemaTO derSchemaTO) { + SyncopeClientCompositeException scce = SyncopeClientException.buildComposite(); + + if (StringUtils.isBlank(derSchemaTO.getExpression())) { + SyncopeClientException requiredValuesMissing = + SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing); + requiredValuesMissing.getElements().add("expression"); + + scce.addException(requiredValuesMissing); + } else if (!JexlUtil.isExpressionValid(derSchemaTO.getExpression())) { + SyncopeClientException e = SyncopeClientException.build(ClientExceptionType.InvalidValues); + e.getElements().add(derSchemaTO.getExpression()); + + scce.addException(e); + } + + if (scce.hasExceptions()) { + throw scce; + } + + BeanUtils.copyProperties(derSchemaTO, derSchema); + + return derSchema; + } + + @Override + public <T extends DerSchema> T create(final DerSchemaTO derSchemaTO, final T derSchema) { + return populate(derSchema, derSchemaTO); + } + + @Override + public <T extends DerSchema> T update(final DerSchemaTO derSchemaTO, final T derSchema) { + return populate(derSchema, derSchemaTO); + } + + @Override + public <T extends DerSchema> DerSchemaTO getDerSchemaTO(final T derSchema) { + DerSchemaTO derSchemaTO = new DerSchemaTO(); + BeanUtils.copyProperties(derSchema, derSchemaTO); + + return derSchemaTO; + } + + // --------------- VIRTUAL ----------------- + private <T extends VirSchema> T fill(final T virSchema, final VirSchemaTO virSchemaTO) { + BeanUtils.copyProperties(virSchemaTO, virSchema); + + return virSchema; + } + + @Override + public <T extends VirSchema> T create(final VirSchemaTO virSchemaTO, final T virSchema) { + return fill(virSchema, virSchemaTO); + } + + @Override + public <T extends VirSchema> T update(final VirSchemaTO virSchemaTO, final T virSchema) { + return fill(virSchema, virSchemaTO); + } + + @Override + public <T extends VirSchema> VirSchemaTO getVirSchemaTO(final T virSchema) { + VirSchemaTO virSchemaTO = new VirSchemaTO(); + BeanUtils.copyProperties(virSchema, virSchemaTO); + + return virSchemaTO; + } +} 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/SecurityQuestionDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SecurityQuestionDataBinderImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SecurityQuestionDataBinderImpl.java new file mode 100644 index 0000000..34523f2 --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SecurityQuestionDataBinderImpl.java @@ -0,0 +1,55 @@ +/* + * 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.SecurityQuestionDataBinder; +import org.apache.syncope.common.lib.to.SecurityQuestionTO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion; +import org.apache.syncope.core.misc.spring.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SecurityQuestionDataBinderImpl implements SecurityQuestionDataBinder { + + @Autowired + private EntityFactory entityFactory; + + @Override + public SecurityQuestionTO getSecurityQuestionTO(final SecurityQuestion securityQuestion) { + SecurityQuestionTO securityQuestionTO = new SecurityQuestionTO(); + + BeanUtils.copyProperties(securityQuestion, securityQuestionTO); + + return securityQuestionTO; + } + + @Override + public SecurityQuestion create(final SecurityQuestionTO securityQuestionTO) { + SecurityQuestion result = entityFactory.newEntity(SecurityQuestion.class); + update(result, securityQuestionTO); + return result; + } + + @Override + public void update(final SecurityQuestion securityQuestion, final SecurityQuestionTO securityQuestionTO) { + BeanUtils.copyProperties(securityQuestionTO, securityQuestion, "key"); + } +} 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/TaskDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java new file mode 100644 index 0000000..091c108 --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java @@ -0,0 +1,343 @@ +/* + * 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.TaskDataBinder; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.AbstractAttributableTO; +import org.apache.syncope.common.lib.to.AbstractProvisioningTaskTO; +import org.apache.syncope.common.lib.to.AbstractTaskTO; +import org.apache.syncope.common.lib.to.AttrTO; +import org.apache.syncope.common.lib.to.MembershipTO; +import org.apache.syncope.common.lib.to.PropagationTaskTO; +import org.apache.syncope.common.lib.to.PushTaskTO; +import org.apache.syncope.common.lib.to.RoleTO; +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.to.UserTO; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.MatchingRule; +import org.apache.syncope.common.lib.types.TaskType; +import org.apache.syncope.common.lib.types.UnmatchingRule; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.TaskExecDAO; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.task.NotificationTask; +import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; +import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask; +import org.apache.syncope.core.persistence.api.entity.task.PushTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.api.entity.task.SyncTask; +import org.apache.syncope.core.persistence.api.entity.task.Task; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.apache.syncope.core.persistence.api.entity.task.TaskUtil; +import org.apache.syncope.core.provisioning.api.job.JobNamer; +import org.apache.syncope.core.misc.spring.BeanUtils; +import org.apache.syncope.core.misc.jexl.JexlUtil; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.Trigger; +import org.quartz.TriggerKey; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; +import org.springframework.stereotype.Component; + +@Component +public class TaskDataBinderImpl implements TaskDataBinder { + + /** + * Logger. + */ + private static final Logger LOG = LoggerFactory.getLogger(TaskDataBinder.class); + + private static final String[] IGNORE_TASK_PROPERTIES = { + "executions", "resource", "matchingRule", "unmatchingRule" }; + + private static final String[] IGNORE_TASK_EXECUTION_PROPERTIES = { "key", "task" }; + + @Autowired + private ExternalResourceDAO resourceDAO; + + @Autowired + private TaskExecDAO taskExecDAO; + + @Autowired + private SchedulerFactoryBean scheduler; + + private void checkJexl(final AbstractAttributableTO attributableTO, final SyncopeClientException sce) { + for (AttrTO attrTO : attributableTO.getPlainAttrs()) { + if (!attrTO.getValues().isEmpty() && !JexlUtil.isExpressionValid(attrTO.getValues().get(0))) { + sce.getElements().add("Invalid JEXL: " + attrTO.getValues().get(0)); + } + } + + for (AttrTO attrTO : attributableTO.getVirAttrs()) { + if (!attrTO.getValues().isEmpty() && !JexlUtil.isExpressionValid(attrTO.getValues().get(0))) { + sce.getElements().add("Invalid JEXL: " + attrTO.getValues().get(0)); + } + } + } + + private void fill(final ProvisioningTask task, final AbstractProvisioningTaskTO taskTO) { + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSyncTask); + + if (task instanceof PushTask && taskTO instanceof PushTaskTO) { + final PushTask pushTask = (PushTask) task; + final PushTaskTO pushTaskTO = (PushTaskTO) taskTO; + + pushTask.setUserFilter(pushTaskTO.getUserFilter()); + pushTask.setRoleFilter(pushTaskTO.getRoleFilter()); + + pushTask.setMatchingRule(pushTaskTO.getMatchingRule() == null + ? MatchingRule.LINK : pushTaskTO.getMatchingRule()); + + pushTask.setUnmatchingRule(pushTaskTO.getUnmatchingRule() == null + ? UnmatchingRule.ASSIGN : pushTaskTO.getUnmatchingRule()); + + } else if (task instanceof SyncTask && taskTO instanceof SyncTaskTO) { + final SyncTask syncTask = (SyncTask) task; + final SyncTaskTO syncTaskTO = (SyncTaskTO) taskTO; + + syncTask.setMatchingRule(syncTaskTO.getMatchingRule() == null + ? MatchingRule.UPDATE : syncTaskTO.getMatchingRule()); + + syncTask.setUnmatchingRule(syncTaskTO.getUnmatchingRule() == null + ? UnmatchingRule.PROVISION : syncTaskTO.getUnmatchingRule()); + + // 1. validate JEXL expressions in user and role templates + if (syncTaskTO.getUserTemplate() != null) { + UserTO template = syncTaskTO.getUserTemplate(); + + if (StringUtils.isNotBlank(template.getUsername()) + && !JexlUtil.isExpressionValid(template.getUsername())) { + + sce.getElements().add("Invalid JEXL: " + template.getUsername()); + } + if (StringUtils.isNotBlank(template.getPassword()) + && !JexlUtil.isExpressionValid(template.getPassword())) { + + sce.getElements().add("Invalid JEXL: " + template.getPassword()); + } + + checkJexl(template, sce); + + for (MembershipTO memb : template.getMemberships()) { + checkJexl(memb, sce); + } + } + if (syncTaskTO.getRoleTemplate() != null) { + RoleTO template = syncTaskTO.getRoleTemplate(); + + if (StringUtils.isNotBlank(template.getName()) && !JexlUtil.isExpressionValid(template.getName())) { + sce.getElements().add("Invalid JEXL: " + template.getName()); + } + + checkJexl(template, sce); + } + if (!sce.isEmpty()) { + throw sce; + } + + // 2. all JEXL expressions are valid: accept user and role templates + syncTask.setUserTemplate(syncTaskTO.getUserTemplate()); + syncTask.setRoleTemplate(syncTaskTO.getRoleTemplate()); + + syncTask.setFullReconciliation(syncTaskTO.isFullReconciliation()); + } + + // 3. fill the remaining fields + task.setPerformCreate(taskTO.isPerformCreate()); + task.setPerformUpdate(taskTO.isPerformUpdate()); + task.setPerformDelete(taskTO.isPerformDelete()); + task.setSyncStatus(taskTO.isSyncStatus()); + task.getActionsClassNames().clear(); + task.getActionsClassNames().addAll(taskTO.getActionsClassNames()); + } + + @Override + public SchedTask createSchedTask(final SchedTaskTO taskTO, final TaskUtil taskUtil) { + final Class<? extends AbstractTaskTO> taskTOClass = taskUtil.taskTOClass(); + + if (taskTOClass == null || !taskTOClass.equals(taskTO.getClass())) { + throw new IllegalArgumentException( + String.format("taskUtil is type %s but task is not: %s", taskTOClass, taskTO.getClass())); + } + + SchedTask task = taskUtil.newTask(); + task.setCronExpression(taskTO.getCronExpression()); + task.setName(taskTO.getName()); + task.setDescription(taskTO.getDescription()); + + if (taskUtil.getType() == TaskType.SCHEDULED) { + task.setJobClassName(taskTO.getJobClassName()); + } else if (taskTO instanceof AbstractProvisioningTaskTO) { + final AbstractProvisioningTaskTO provisioningTaskTO = (AbstractProvisioningTaskTO) taskTO; + + ExternalResource resource = resourceDAO.find(provisioningTaskTO.getResource()); + if (resource == null) { + throw new NotFoundException("Resource " + provisioningTaskTO.getResource()); + } + ((ProvisioningTask) task).setResource(resource); + + fill((ProvisioningTask) task, provisioningTaskTO); + } + + return task; + } + + @Override + public void updateSchedTask(final SchedTask task, final SchedTaskTO taskTO, final TaskUtil taskUtil) { + Class<? extends Task> taskClass = taskUtil.taskClass(); + Class<? extends AbstractTaskTO> taskTOClass = taskUtil.taskTOClass(); + + if (taskClass == null || !taskClass.isAssignableFrom(task.getClass())) { + throw new IllegalArgumentException( + String.format("taskUtil is type %s but task is not: %s", taskClass, task.getClass())); + } + + if (taskTOClass == null || !taskTOClass.equals(taskTO.getClass())) { + throw new IllegalArgumentException( + String.format("taskUtil is type %s but task is not: %s", taskTOClass, taskTO.getClass())); + } + + task.setCronExpression(taskTO.getCronExpression()); + if (StringUtils.isNotBlank(taskTO.getName())) { + task.setName(taskTO.getName()); + } + if (StringUtils.isNotBlank(taskTO.getDescription())) { + task.setDescription(taskTO.getDescription()); + } + + if (task instanceof ProvisioningTask) { + fill((ProvisioningTask) task, (AbstractProvisioningTaskTO) taskTO); + } + } + + @Override + public TaskExecTO getTaskExecTO(final TaskExec execution) { + TaskExecTO executionTO = new TaskExecTO(); + BeanUtils.copyProperties(execution, executionTO, IGNORE_TASK_EXECUTION_PROPERTIES); + + if (execution.getKey() != null) { + executionTO.setKey(execution.getKey()); + } + + if (execution.getTask() != null && execution.getTask().getKey() != null) { + executionTO.setTask(execution.getTask().getKey()); + } + + return executionTO; + } + + private void setExecTime(final SchedTaskTO taskTO, final Task task) { + String triggerName = JobNamer.getTriggerName(JobNamer.getJobName(task)); + + Trigger trigger = null; + try { + trigger = scheduler.getScheduler().getTrigger(new TriggerKey(triggerName, Scheduler.DEFAULT_GROUP)); + } catch (SchedulerException e) { + LOG.warn("While trying to get to " + triggerName, e); + } + + if (trigger != null) { + taskTO.setLastExec(trigger.getPreviousFireTime()); + taskTO.setNextExec(trigger.getNextFireTime()); + } + } + + @Override + public <T extends AbstractTaskTO> T getTaskTO(final Task task, final TaskUtil taskUtil) { + T taskTO = taskUtil.newTaskTO(); + BeanUtils.copyProperties(task, taskTO, IGNORE_TASK_PROPERTIES); + + TaskExec latestExec = taskExecDAO.findLatestStarted(task); + taskTO.setLatestExecStatus(latestExec == null ? "" : latestExec.getStatus()); + taskTO.setStartDate(latestExec == null ? null : latestExec.getStartDate()); + taskTO.setEndDate(latestExec == null ? null : latestExec.getEndDate()); + + for (TaskExec execution : task.getExecs()) { + taskTO.getExecutions().add(getTaskExecTO(execution)); + } + + switch (taskUtil.getType()) { + case PROPAGATION: + if (!(task instanceof PropagationTask)) { + throw new IllegalArgumentException("taskUtil is type Propagation but task is not PropagationTask: " + + task.getClass().getName()); + } + ((PropagationTaskTO) taskTO).setResource(((PropagationTask) task).getResource().getKey()); + break; + + case SCHEDULED: + if (!(task instanceof SchedTask)) { + throw new IllegalArgumentException("taskUtil is type Sched but task is not SchedTask: " + + task.getClass().getName()); + } + setExecTime((SchedTaskTO) taskTO, task); + ((SchedTaskTO) taskTO).setName(((SchedTask) task).getName()); + ((SchedTaskTO) taskTO).setDescription(((SchedTask) task).getDescription()); + break; + + case SYNCHRONIZATION: + if (!(task instanceof SyncTask)) { + throw new IllegalArgumentException("taskUtil is type Sync but task is not SyncTask: " + + task.getClass().getName()); + } + setExecTime((SchedTaskTO) taskTO, task); + ((SyncTaskTO) taskTO).setName(((SyncTask) task).getName()); + ((SyncTaskTO) taskTO).setDescription(((SyncTask) task).getDescription()); + ((SyncTaskTO) taskTO).setResource(((SyncTask) task).getResource().getKey()); + ((SyncTaskTO) taskTO).setMatchingRule(((SyncTask) task).getMatchingRule() == null + ? MatchingRule.UPDATE : ((SyncTask) task).getMatchingRule()); + ((SyncTaskTO) taskTO).setUnmatchingRule(((SyncTask) task).getUnmatchingRule() == null + ? UnmatchingRule.PROVISION : ((SyncTask) task).getUnmatchingRule()); + break; + + case PUSH: + if (!(task instanceof PushTask)) { + throw new IllegalArgumentException("taskUtil is type Push but task is not PushTask: " + + task.getClass().getName()); + } + setExecTime((SchedTaskTO) taskTO, task); + ((PushTaskTO) taskTO).setName(((PushTask) task).getName()); + ((PushTaskTO) taskTO).setDescription(((PushTask) task).getDescription()); + ((PushTaskTO) taskTO).setResource(((PushTask) task).getResource().getKey()); + ((PushTaskTO) taskTO).setMatchingRule(((PushTask) task).getMatchingRule() == null + ? MatchingRule.LINK : ((PushTask) task).getMatchingRule()); + ((PushTaskTO) taskTO).setUnmatchingRule(((PushTask) task).getUnmatchingRule() == null + ? UnmatchingRule.ASSIGN : ((PushTask) task).getUnmatchingRule()); + break; + + case NOTIFICATION: + if (((NotificationTask) task).isExecuted() && StringUtils.isBlank(taskTO.getLatestExecStatus())) { + taskTO.setLatestExecStatus("[EXECUTED]"); + } + break; + + default: + } + + return taskTO; + } +} 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/UserDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java new file mode 100644 index 0000000..af6dbb6 --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java @@ -0,0 +1,411 @@ +/* + * 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.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import javax.annotation.Resource; +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.MembershipMod; +import org.apache.syncope.common.lib.mod.UserMod; +import org.apache.syncope.common.lib.to.MembershipTO; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AttributableType; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.core.persistence.api.dao.ConfDAO; +import org.apache.syncope.core.persistence.api.dao.SecurityQuestionDAO; +import org.apache.syncope.core.persistence.api.entity.DerAttr; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.VirAttr; +import org.apache.syncope.core.persistence.api.entity.membership.MDerAttr; +import org.apache.syncope.core.persistence.api.entity.membership.MPlainAttr; +import org.apache.syncope.core.persistence.api.entity.membership.MVirAttr; +import org.apache.syncope.core.persistence.api.entity.membership.Membership; +import org.apache.syncope.core.persistence.api.entity.role.Role; +import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.common.lib.types.PropagationByResource; +import org.apache.syncope.core.provisioning.api.data.UserDataBinder; +import org.apache.syncope.core.misc.security.AuthContextUtil; +import org.apache.syncope.core.misc.security.Encryptor; +import org.apache.syncope.core.misc.spring.BeanUtils; +import org.apache.syncope.core.misc.ConnObjectUtil; +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 UserDataBinderImpl extends AbstractAttributableDataBinder implements UserDataBinder { + + private static final String[] IGNORE_USER_PROPERTIES = { + "memberships", "plainAttrs", "derAttrs", "virAttrs", "resources", "securityQuestion", "securityAnswer" + }; + + @Autowired + private ConfDAO confDAO; + + @Autowired + private ConnObjectUtil connObjectUtil; + + @Autowired + private SecurityQuestionDAO securityQuestionDAO; + + @Resource(name = "adminUser") + private String adminUser; + + @Resource(name = "anonymousUser") + private String anonymousUser; + + private final Encryptor encryptor = Encryptor.getInstance(); + + @Transactional(readOnly = true) + @Override + public UserTO getAuthenticatedUserTO() { + final UserTO authUserTO; + + final String authUsername = AuthContextUtil.getAuthenticatedUsername(); + if (anonymousUser.equals(authUsername)) { + authUserTO = new UserTO(); + authUserTO.setKey(-2); + authUserTO.setUsername(anonymousUser); + } else if (adminUser.equals(authUsername)) { + authUserTO = new UserTO(); + authUserTO.setKey(-1); + authUserTO.setUsername(adminUser); + } else { + User authUser = userDAO.find(authUsername); + authUserTO = getUserTO(authUser); + } + + return authUserTO; + } + + @Transactional(readOnly = true) + @Override + public boolean verifyPassword(final String username, final String password) { + return verifyPassword(userDAO.authFetch(username), password); + } + + @Transactional(readOnly = true) + @Override + public boolean verifyPassword(final User user, final String password) { + return encryptor.verify(password, user.getCipherAlgorithm(), user.getPassword()); + } + + private void setPassword(final User user, final String password, + final SyncopeClientCompositeException scce) { + + try { + final String algorithm = confDAO.find( + "password.cipher.algorithm", CipherAlgorithm.AES.name()).getValues().get(0).getStringValue(); + CipherAlgorithm predefined = CipherAlgorithm.valueOf(algorithm); + user.setPassword(password, predefined); + } catch (IllegalArgumentException e) { + final SyncopeClientException invalidCiperAlgorithm = + SyncopeClientException.build(ClientExceptionType.NotFound); + invalidCiperAlgorithm.getElements().add(e.getMessage()); + scce.addException(invalidCiperAlgorithm); + + throw scce; + } + } + + @Override + public void create(final User user, final UserTO userTO, final boolean storePassword) { + SyncopeClientCompositeException scce = SyncopeClientException.buildComposite(); + + // memberships + Role role; + for (MembershipTO membershipTO : userTO.getMemberships()) { + role = roleDAO.find(membershipTO.getRoleId()); + + if (role == null) { + if (LOG.isDebugEnabled()) { + LOG.debug("Ignoring invalid role " + membershipTO.getRoleName()); + } + } else { + Membership membership = null; + if (user.getKey() != null) { + membership = user.getMembership(role.getKey()) == null + ? membershipDAO.find(user, role) + : user.getMembership(role.getKey()); + } + if (membership == null) { + membership = entityFactory.newEntity(Membership.class); + membership.setRole(role); + membership.setUser(user); + + user.addMembership(membership); + } + + fill(membership, membershipTO, attrUtilFactory.getInstance(AttributableType.MEMBERSHIP), scce); + } + } + + // attributes, derived attributes, virtual attributes and resources + fill(user, userTO, attrUtilFactory.getInstance(AttributableType.USER), scce); + + // set password + if (StringUtils.isBlank(userTO.getPassword()) || !storePassword) { + LOG.debug("Password was not provided or not required to be stored"); + } else { + setPassword(user, userTO.getPassword(), scce); + } + + // set username + user.setUsername(userTO.getUsername()); + + // security question / answer + if (userTO.getSecurityQuestion() != null) { + SecurityQuestion securityQuestion = securityQuestionDAO.find(userTO.getSecurityQuestion()); + if (securityQuestion != null) { + user.setSecurityQuestion(securityQuestion); + } + } + user.setSecurityAnswer(userTO.getSecurityAnswer()); + } + + /** + * Update user, given UserMod. + * + * @param toBeUpdated user to be updated + * @param userMod bean containing update request + * @return updated user + propagation by resource + * @see PropagationByResource + */ + @Override + public PropagationByResource update(final User toBeUpdated, final UserMod userMod) { + // Re-merge any pending change from workflow tasks + User user = userDAO.save(toBeUpdated); + + PropagationByResource propByRes = new PropagationByResource(); + + SyncopeClientCompositeException scce = SyncopeClientException.buildComposite(); + + Set<String> currentResources = user.getResourceNames(); + + // fetch account ids before update + Map<String, String> oldAccountIds = getAccountIds(user, AttributableType.USER); + + // password + if (StringUtils.isNotBlank(userMod.getPassword())) { + setPassword(user, userMod.getPassword(), scce); + user.setChangePwdDate(new Date()); + propByRes.addAll(ResourceOperation.UPDATE, currentResources); + } + + // username + if (userMod.getUsername() != null && !userMod.getUsername().equals(user.getUsername())) { + propByRes.addAll(ResourceOperation.UPDATE, currentResources); + + user.setUsername(userMod.getUsername()); + } + + // security question / answer: + // userMod.getSecurityQuestion() is null => remove user security question and answer + // userMod.getSecurityQuestion() == 0 => don't change anything + // userMod.getSecurityQuestion() > 0 => update user security question and answer + if (userMod.getSecurityQuestion() == null) { + user.setSecurityQuestion(null); + user.setSecurityAnswer(null); + } else if (userMod.getSecurityQuestion() > 0) { + SecurityQuestion securityQuestion = securityQuestionDAO.find(userMod.getSecurityQuestion()); + if (securityQuestion != null) { + user.setSecurityQuestion(securityQuestion); + user.setSecurityAnswer(userMod.getSecurityAnswer()); + } + } + + // attributes, derived attributes, virtual attributes and resources + propByRes.merge(fill(user, userMod, attrUtilFactory.getInstance(AttributableType.USER), scce)); + + // store the role ids of membership required to be added + Set<Long> membershipToBeAddedRoleIds = new HashSet<>(); + for (MembershipMod membToBeAdded : userMod.getMembershipsToAdd()) { + membershipToBeAddedRoleIds.add(membToBeAdded.getRole()); + } + + final Set<String> toBeDeprovisioned = new HashSet<>(); + final Set<String> toBeProvisioned = new HashSet<>(); + + // memberships to be removed + for (Long membershipId : userMod.getMembershipsToRemove()) { + LOG.debug("Membership to be removed: {}", membershipId); + + Membership membership = membershipDAO.find(membershipId); + if (membership == null) { + LOG.debug("Invalid membership id specified to be removed: {}", membershipId); + } else { + if (!membershipToBeAddedRoleIds.contains(membership.getRole().getKey())) { + toBeDeprovisioned.addAll(membership.getRole().getResourceNames()); + } + + // In order to make the removeMembership() below to work, + // we need to be sure to take exactly the same membership + // of the user object currently in memory (which has potentially + // some modifications compared to the one stored in the DB + membership = user.getMembership(membership.getRole().getKey()); + if (membershipToBeAddedRoleIds.contains(membership.getRole().getKey())) { + Set<Long> attributeIds = new HashSet<>(membership.getPlainAttrs().size()); + for (PlainAttr attribute : membership.getPlainAttrs()) { + attributeIds.add(attribute.getKey()); + } + for (Long attributeId : attributeIds) { + plainAttrDAO.delete(attributeId, MPlainAttr.class); + } + attributeIds.clear(); + + // remove derived attributes + for (DerAttr derAttr : membership.getDerAttrs()) { + attributeIds.add(derAttr.getKey()); + } + for (Long derAttrId : attributeIds) { + derAttrDAO.delete(derAttrId, MDerAttr.class); + } + attributeIds.clear(); + + // remove virtual attributes + for (VirAttr virAttr : membership.getVirAttrs()) { + attributeIds.add(virAttr.getKey()); + } + for (Long virAttrId : attributeIds) { + virAttrDAO.delete(virAttrId, MVirAttr.class); + } + attributeIds.clear(); + } else { + user.removeMembership(membership); + + membershipDAO.delete(membershipId); + } + } + } + + // memberships to be added + for (MembershipMod membershipMod : userMod.getMembershipsToAdd()) { + LOG.debug("Membership to be added: role({})", membershipMod.getRole()); + + Role role = roleDAO.find(membershipMod.getRole()); + if (role == null) { + LOG.debug("Ignoring invalid role {}", membershipMod.getRole()); + } else { + Membership membership = user.getMembership(role.getKey()); + if (membership == null) { + membership = entityFactory.newEntity(Membership.class); + membership.setRole(role); + membership.setUser(user); + + user.addMembership(membership); + + toBeProvisioned.addAll(role.getResourceNames()); + } + + propByRes.merge(fill(membership, membershipMod, + attrUtilFactory.getInstance(AttributableType.MEMBERSHIP), scce)); + } + } + + propByRes.addAll(ResourceOperation.DELETE, toBeDeprovisioned); + propByRes.addAll(ResourceOperation.UPDATE, toBeProvisioned); + + /** + * In case of new memberships all the current resources have to be updated in order to propagate new role and + * membership attribute values. + */ + if (!toBeDeprovisioned.isEmpty() || !toBeProvisioned.isEmpty()) { + currentResources.removeAll(toBeDeprovisioned); + propByRes.addAll(ResourceOperation.UPDATE, currentResources); + } + + // check if some account id was changed by the update above + Map<String, String> newAccountIds = getAccountIds(user, AttributableType.USER); + for (Map.Entry<String, String> entry : oldAccountIds.entrySet()) { + if (newAccountIds.containsKey(entry.getKey()) + && !entry.getValue().equals(newAccountIds.get(entry.getKey()))) { + + propByRes.addOldAccountId(entry.getKey(), entry.getValue()); + propByRes.add(ResourceOperation.UPDATE, entry.getKey()); + } + } + + return propByRes; + } + + @Transactional(readOnly = true) + @Override + public UserTO getUserTO(final User user) { + UserTO userTO = new UserTO(); + + BeanUtils.copyProperties(user, userTO, IGNORE_USER_PROPERTIES); + + if (user.getSecurityQuestion() != null) { + userTO.setSecurityQuestion(user.getSecurityQuestion().getKey()); + } + + connObjectUtil.retrieveVirAttrValues(user, attrUtilFactory.getInstance(AttributableType.USER)); + fillTO(userTO, user.getPlainAttrs(), user.getDerAttrs(), user.getVirAttrs(), user.getResources()); + + MembershipTO membershipTO; + for (Membership membership : user.getMemberships()) { + membershipTO = new MembershipTO(); + + // set sys info + membershipTO.setCreator(membership.getCreator()); + membershipTO.setCreationDate(membership.getCreationDate()); + membershipTO.setLastModifier(membership.getLastModifier()); + membershipTO.setLastChangeDate(membership.getLastChangeDate()); + + membershipTO.setKey(membership.getKey()); + membershipTO.setRoleId(membership.getRole().getKey()); + membershipTO.setRoleName(membership.getRole().getName()); + + // SYNCOPE-458 retrieve also membership virtual attributes + connObjectUtil.retrieveVirAttrValues(membership, attrUtilFactory.getInstance(AttributableType.MEMBERSHIP)); + + fillTO(membershipTO, + membership.getPlainAttrs(), membership.getDerAttrs(), membership.getVirAttrs(), + Collections.<ExternalResource>emptyList()); + + userTO.getMemberships().add(membershipTO); + } + + return userTO; + } + + @Transactional(readOnly = true) + @Override + public UserTO getUserTO(final String username) { + return getUserTO(userDAO.authFetch(username)); + } + + @Transactional(readOnly = true) + @Override + public UserTO getUserTO(final Long key) { + return getUserTO(userDAO.authFetch(key)); + } + +}
