/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.syncope.core.rest.data;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.SyncopeClientCompositeException;
import org.apache.syncope.common.SyncopeClientException;
import org.apache.syncope.common.mod.AbstractAttributableMod;
import org.apache.syncope.common.mod.AbstractSubjectMod;
import org.apache.syncope.common.mod.AttributeMod;
import org.apache.syncope.common.to.AbstractAttributableTO;
import org.apache.syncope.common.to.AbstractSubjectTO;
import org.apache.syncope.common.to.AttributeTO;
import org.apache.syncope.common.types.AttributableType;
import org.apache.syncope.common.types.ClientExceptionType;
import org.apache.syncope.common.types.IntMappingType;
import org.apache.syncope.common.types.MappingPurpose;
import org.apache.syncope.common.types.ResourceOperation;
import org.apache.syncope.core.persistence.beans.AbstractAttr;
import org.apache.syncope.core.persistence.beans.AbstractAttrValue;
import org.apache.syncope.core.persistence.beans.AbstractAttributable;
import org.apache.syncope.core.persistence.beans.AbstractDerAttr;
import org.apache.syncope.core.persistence.beans.AbstractDerSchema;
import org.apache.syncope.core.persistence.beans.AbstractMappingItem;
import org.apache.syncope.core.persistence.beans.AbstractNormalSchema;
import org.apache.syncope.core.persistence.beans.AbstractSchema;
import org.apache.syncope.core.persistence.beans.AbstractSubject;
import org.apache.syncope.core.persistence.beans.AbstractVirAttr;
import org.apache.syncope.core.persistence.beans.AbstractVirSchema;
import org.apache.syncope.core.persistence.beans.ExternalResource;
import org.apache.syncope.core.persistence.beans.membership.MAttr;
import org.apache.syncope.core.persistence.beans.membership.MAttrTemplate;
import org.apache.syncope.core.persistence.beans.membership.MDerAttr;
import org.apache.syncope.core.persistence.beans.membership.MDerAttrTemplate;
import org.apache.syncope.core.persistence.beans.membership.MVirAttr;
import org.apache.syncope.core.persistence.beans.membership.MVirAttrTemplate;
import org.apache.syncope.core.persistence.beans.membership.Membership;
import org.apache.syncope.core.persistence.beans.role.RAttr;
import org.apache.syncope.core.persistence.beans.role.RAttrTemplate;
import org.apache.syncope.core.persistence.beans.role.RDerAttr;
import org.apache.syncope.core.persistence.beans.role.RDerAttrTemplate;
import org.apache.syncope.core.persistence.beans.role.RVirAttr;
import org.apache.syncope.core.persistence.beans.role.RVirAttrTemplate;
import org.apache.syncope.core.persistence.beans.role.SyncopeRole;
import org.apache.syncope.core.persistence.beans.user.UAttr;
import org.apache.syncope.core.persistence.beans.user.UDerAttr;
import org.apache.syncope.core.persistence.beans.user.UDerSchema;
import org.apache.syncope.core.persistence.beans.user.USchema;
import org.apache.syncope.core.persistence.beans.user.UVirAttr;
import org.apache.syncope.core.persistence.beans.user.UVirSchema;
import org.apache.syncope.core.persistence.dao.AttrDAO;
import org.apache.syncope.core.persistence.dao.AttrValueDAO;
import org.apache.syncope.core.persistence.dao.ConfDAO;
import org.apache.syncope.core.persistence.dao.DerAttrDAO;
import org.apache.syncope.core.persistence.dao.DerSchemaDAO;
import org.apache.syncope.core.persistence.dao.MembershipDAO;
import org.apache.syncope.core.persistence.dao.PolicyDAO;
import org.apache.syncope.core.persistence.dao.ResourceDAO;
import org.apache.syncope.core.persistence.dao.RoleDAO;
import org.apache.syncope.core.persistence.dao.SchemaDAO;
import org.apache.syncope.core.persistence.dao.UserDAO;
import org.apache.syncope.core.persistence.dao.VirAttrDAO;
import org.apache.syncope.core.persistence.dao.VirSchemaDAO;
import org.apache.syncope.core.persistence.validation.attrvalue.InvalidAttrValueException;
import org.apache.syncope.core.propagation.PropagationByResource;
import org.apache.syncope.core.util.AttributableUtil;
import org.apache.syncope.core.util.MappingUtil;
import org.apache.syncope.core.util.jexl.JexlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
public abstract class AbstractAttributableDataBinder {
/**
* Logger.
*/
protected static final Logger LOG = LoggerFactory.getLogger(AbstractAttributableDataBinder.class);
@Autowired
protected ConfDAO confDAO;
@Autowired
protected RoleDAO roleDAO;
@Autowired
protected SchemaDAO schemaDAO;
@Autowired
protected DerSchemaDAO derSchemaDAO;
@Autowired
protected VirSchemaDAO virSchemaDAO;
@Autowired
protected AttrDAO attrDAO;
@Autowired
protected DerAttrDAO derAttrDAO;
@Autowired
protected VirAttrDAO virAttrDAO;
@Autowired
protected AttrValueDAO attributeValueDAO;
@Autowired
protected UserDAO userDAO;
@Autowired
protected ResourceDAO resourceDAO;
@Autowired
protected MembershipDAO membershipDAO;
@Autowired
protected PolicyDAO policyDAO;
@SuppressWarnings("unchecked")
protected <T extends AbstractSchema> T getSchema(final String schemaName, final Class<T> reference) {
T result = null;
if (AbstractNormalSchema.class.isAssignableFrom(reference)) {
result = (T) getNormalSchema(schemaName, (Class<? extends AbstractNormalSchema>) reference);
} else if (AbstractDerSchema.class.isAssignableFrom(reference)) {
result = (T) getDerSchema(schemaName, (Class<? extends AbstractDerSchema>) reference);
} else if (AbstractVirSchema.class.isAssignableFrom(reference)) {
result = (T) getVirSchema(schemaName, (Class<? extends AbstractVirSchema>) reference);
}
return result;
}
protected <T extends AbstractNormalSchema> T getNormalSchema(final String schemaName, final Class<T> reference) {
T schema = null;
if (StringUtils.isNotBlank(schemaName)) {
schema = schemaDAO.find(schemaName, reference);
// safely ignore invalid schemas from AttributeTO
// 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 AbstractDerSchema> 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;
}
private <T extends AbstractVirSchema> 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;
}
private ExternalResource getResource(final String resourceName) {
ExternalResource resource = resourceDAO.find(resourceName);
if (resource == null) {
LOG.debug("Ignoring invalid resource {} ", resourceName);
}
return resource;
}
protected void fillAttribute(final List<String> values, final AttributableUtil attributableUtil,
final AbstractNormalSchema schema, final AbstractAttr 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.getName());
} else {
try {
attr.addValue(value, attributableUtil);
} catch (InvalidAttrValueException e) {
LOG.error("Invalid value for attribute " + schema.getName() + ": " + value, e);
invalidValues.getElements().add(schema.getName() + ": " + value + " - " + e.getMessage());
}
}
}
}
private boolean evaluateMandatoryCondition(final AttributableUtil attrUtil, final ExternalResource resource,
final AbstractAttributable attributable, final String intAttrName, final IntMappingType intMappingType) {
boolean result = false;
final List<AbstractMappingItem> mappings = MappingUtil.getMatchingMappingItems(
attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION), intAttrName, intMappingType);
for (Iterator<AbstractMappingItem> itor = mappings.iterator(); itor.hasNext() && !result;) {
final AbstractMappingItem mapping = itor.next();
result |= JexlUtil.evaluateMandatoryCondition(mapping.getMandatoryCondition(), attributable);
}
return result;
}
private boolean evaluateMandatoryCondition(final AttributableUtil attrUtil,
final AbstractAttributable attributable, final String intAttrName, final IntMappingType intMappingType) {
boolean result = false;
if (attributable instanceof AbstractSubject) {
for (Iterator<ExternalResource> itor = ((AbstractSubject) 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 AbstractAttributable attributable) {
SyncopeClientException reqValMissing = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
// Check if there is some mandatory schema defined for which no value has been provided
List<? extends AbstractNormalSchema> normalSchemas;
switch (attrUtil.getType()) {
case ROLE:
normalSchemas = ((SyncopeRole) attributable).getAttrTemplateSchemas(RAttrTemplate.class);
break;
case MEMBERSHIP:
normalSchemas = ((Membership) attributable).getSyncopeRole().
getAttrTemplateSchemas(MAttrTemplate.class);
break;
case USER:
default:
normalSchemas = schemaDAO.findAll(attrUtil.schemaClass());
}
for (AbstractNormalSchema schema : normalSchemas) {
if (attributable.getAttr(schema.getName()) == null
&& !schema.isReadonly()
&& (JexlUtil.evaluateMandatoryCondition(schema.getMandatoryCondition(), attributable)
|| evaluateMandatoryCondition(attrUtil, attributable, schema.getName(),
attrUtil.intMappingType()))) {
LOG.error("Mandatory schema " + schema.getName() + " not provided with values");
reqValMissing.getElements().add(schema.getName());
}
}
List<? extends AbstractDerSchema> derSchemas;
switch (attrUtil.getType()) {
case ROLE:
derSchemas = ((SyncopeRole) attributable).getAttrTemplateSchemas(RDerAttrTemplate.class);
break;
case MEMBERSHIP:
derSchemas = ((Membership) attributable).getSyncopeRole().
getAttrTemplateSchemas(MDerAttrTemplate.class);
break;
case USER:
default:
derSchemas = derSchemaDAO.findAll(attrUtil.derSchemaClass());
}
for (AbstractDerSchema derSchema : derSchemas) {
if (attributable.getDerAttr(derSchema.getName()) == null
&& evaluateMandatoryCondition(attrUtil, attributable, derSchema.getName(),
attrUtil.derIntMappingType())) {
LOG.error("Mandatory derived schema " + derSchema.getName() + " does not evaluate to any value");
reqValMissing.getElements().add(derSchema.getName());
}
}
List<? extends AbstractVirSchema> virSchemas;
switch (attrUtil.getType()) {
case ROLE:
virSchemas = ((SyncopeRole) attributable).getAttrTemplateSchemas(RVirAttrTemplate.class);
break;
case MEMBERSHIP:
virSchemas = ((Membership) attributable).getSyncopeRole().
getAttrTemplateSchemas(MVirAttrTemplate.class);
break;
case USER:
default:
virSchemas = virSchemaDAO.findAll(attrUtil.virSchemaClass());
}
for (AbstractVirSchema virSchema : virSchemas) {
if (attributable.getVirAttr(virSchema.getName()) == null
&& !virSchema.isReadonly()
&& evaluateMandatoryCondition(attrUtil, attributable, virSchema.getName(),
attrUtil.virIntMappingType())) {
LOG.error("Mandatory virtual schema " + virSchema.getName() + " not provided with values");
reqValMissing.getElements().add(virSchema.getName());
}
}
return reqValMissing;
}
private void setAttrSchema(final AbstractAttributable attributable,
final AbstractAttr attr, final AbstractNormalSchema schema) {
if (attr instanceof UAttr) {
((UAttr) attr).setSchema((USchema) schema);
} else if (attr instanceof RAttr) {
RAttrTemplate template = ((SyncopeRole) attributable).
getAttrTemplate(RAttrTemplate.class, schema.getName());
if (template != null) {
((RAttr) attr).setTemplate(template);
}
} else if (attr instanceof MAttr) {
MAttrTemplate template = ((Membership) attributable).getSyncopeRole().
getAttrTemplate(MAttrTemplate.class, schema.getName());
if (template != null) {
((MAttr) attr).setTemplate(template);
}
}
}
private void setDerAttrSchema(final AbstractAttributable attributable,
final AbstractDerAttr derAttr, final AbstractDerSchema derSchema) {
if (derAttr instanceof UDerAttr) {
((UDerAttr) derAttr).setSchema((UDerSchema) derSchema);
} else if (derAttr instanceof RDerAttr) {
RDerAttrTemplate template = ((SyncopeRole) attributable).
getAttrTemplate(RDerAttrTemplate.class, derSchema.getName());
if (template != null) {
((RDerAttr) derAttr).setTemplate(template);
}
} else if (derAttr instanceof MDerAttr) {
MDerAttrTemplate template = ((Membership) attributable).getSyncopeRole().
getAttrTemplate(MDerAttrTemplate.class, derSchema.getName());
if (template != null) {
((MDerAttr) derAttr).setTemplate(template);
}
}
}
private void setVirAttrSchema(final AbstractAttributable attributable,
final AbstractVirAttr virAttr, final AbstractVirSchema virSchema) {
if (virAttr instanceof UVirAttr) {
((UVirAttr) virAttr).setSchema((UVirSchema) virSchema);
} else if (virAttr instanceof RVirAttr) {
RVirAttrTemplate template = ((SyncopeRole) attributable).
getAttrTemplate(RVirAttrTemplate.class, virSchema.getName());
if (template != null) {
((RVirAttr) virAttr).setTemplate(template);
}
} else if (virAttr instanceof MVirAttr) {
MVirAttrTemplate template =
((Membership) attributable).getSyncopeRole().
getAttrTemplate(MVirAttrTemplate.class, virSchema.getName());
if (template != null) {
((MVirAttr) virAttr).setTemplate(template);
}
}
}
public PropagationByResource fillVirtual(final AbstractAttributable attributable,
final Set<String> vAttrsToBeRemoved, final Set<AttributeMod> vAttrsToBeUpdated,
final AttributableUtil attrUtil) {
PropagationByResource propByRes = new PropagationByResource();
final Set<ExternalResource> externalResources = new HashSet<ExternalResource>();
if (attributable instanceof AbstractSubject) {
externalResources.addAll(((AbstractSubject) attributable).getResources());
}
if (attributable instanceof Membership) {
externalResources.clear();
externalResources.addAll(((Membership) attributable).getSyncopeUser().getResources());
}
// 1. virtual attributes to be removed
for (String vAttrToBeRemoved : vAttrsToBeRemoved) {
AbstractVirSchema virSchema = getVirSchema(vAttrToBeRemoved, attrUtil.virSchemaClass());
if (virSchema != null) {
AbstractVirAttr virAttr = attributable.getVirAttr(virSchema.getName());
if (virAttr == null) {
LOG.debug("No virtual attribute found for schema {}", virSchema.getName());
} else {
attributable.removeVirAttr(virAttr);
virAttrDAO.delete(virAttr);
}
for (ExternalResource resource : resourceDAO.findAll()) {
for (AbstractMappingItem mapItem : attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION)) {
if (virSchema.getName().equals(mapItem.getIntAttrName())
&& mapItem.getIntMappingType() == attrUtil.virIntMappingType()
&& externalResources.contains(resource)) {
propByRes.add(ResourceOperation.UPDATE, resource.getName());
// Using virtual attribute as AccountId must be avoided
if (mapItem.isAccountid() && virAttr != null && !virAttr.getValues().isEmpty()) {
propByRes.addOldAccountId(resource.getName(), virAttr.getValues().get(0));
}
}
}
}
}
}
LOG.debug("Virtual attributes to be removed:\n{}", propByRes);
// 2. virtual attributes to be updated
for (AttributeMod vAttrToBeUpdated : vAttrsToBeUpdated) {
AbstractVirSchema virSchema = getVirSchema(vAttrToBeUpdated.getSchema(), attrUtil.virSchemaClass());
AbstractVirAttr virAttr = null;
if (virSchema != null) {
virAttr = attributable.getVirAttr(virSchema.getName());
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 (AbstractMappingItem mapItem : attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION)) {
if (virSchema.getName().equals(mapItem.getIntAttrName())
&& mapItem.getIntMappingType() == attrUtil.virIntMappingType()
&& externalResources.contains(resource)) {
propByRes.add(ResourceOperation.UPDATE, resource.getName());
}
}
}
final List<String> values = new ArrayList<String>(virAttr.getValues());
values.removeAll(vAttrToBeUpdated.getValuesToBeRemoved());
values.addAll(vAttrToBeUpdated.getValuesToBeAdded());
virAttr.setValues(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;
}
protected PropagationByResource fill(final AbstractAttributable attributable,
final AbstractAttributableMod attributableMod, final AttributableUtil attrUtil,
final SyncopeClientCompositeException scce) {
PropagationByResource propByRes = new PropagationByResource();
SyncopeClientException invalidValues = SyncopeClientException.build(ClientExceptionType.InvalidValues);
if (attributable instanceof AbstractSubject && attributableMod instanceof AbstractSubjectMod) {
// 1. resources to be removed
for (String resourceToBeRemoved : ((AbstractSubjectMod) attributableMod).getResourcesToRemove()) {
ExternalResource resource = getResource(resourceToBeRemoved);
if (resource != null) {
propByRes.add(ResourceOperation.DELETE, resource.getName());
((AbstractSubject) attributable).removeResource(resource);
}
}
LOG.debug("Resources to be removed:\n{}", propByRes);
// 2. resources to be added
for (String resourceToBeAdded : ((AbstractSubjectMod) attributableMod).getResourcesToAdd()) {
ExternalResource resource = getResource(resourceToBeAdded);
if (resource != null) {
propByRes.add(ResourceOperation.CREATE, resource.getName());
((AbstractSubject) attributable).addResource(resource);
}
}
LOG.debug("Resources to be added:\n{}", propByRes);
}
// 3. attributes to be removed
for (String attributeToBeRemoved : attributableMod.getAttrsToRemove()) {
AbstractNormalSchema schema = getNormalSchema(attributeToBeRemoved, attrUtil.schemaClass());
if (schema != null) {
AbstractAttr attr = attributable.getAttr(schema.getName());
if (attr == null) {
LOG.debug("No attribute found for schema {}", schema);
} else {
String newValue = null;
for (AttributeMod mod : attributableMod.getAttrsToUpdate()) {
if (schema.getName().equals(mod.getSchema())) {
newValue = mod.getValuesToBeAdded().get(0);
}
}
if (!schema.isUniqueConstraint()
|| (!attr.getUniqueValue().getStringValue().equals(newValue))) {
attributable.removeAttr(attr);
attrDAO.delete(attr.getId(), attrUtil.attrClass());
}
}
if (attributable instanceof AbstractSubject) {
for (ExternalResource resource : resourceDAO.findAll()) {
for (AbstractMappingItem mapItem : attrUtil.
getMappingItems(resource, MappingPurpose.PROPAGATION)) {
if (schema.getName().equals(mapItem.getIntAttrName())
&& mapItem.getIntMappingType() == attrUtil.intMappingType()
&& ((AbstractSubject) attributable).getResources().contains(resource)) {
propByRes.add(ResourceOperation.UPDATE, resource.getName());
if (mapItem.isAccountid() && attr != null
&& !attr.getValuesAsStrings().isEmpty()) {
propByRes.addOldAccountId(resource.getName(),
attr.getValuesAsStrings().iterator().next());
}
}
}
}
}
}
}
LOG.debug("Attributes to be removed:\n{}", propByRes);
// 4. attributes to be updated
for (AttributeMod attributeMod : attributableMod.getAttrsToUpdate()) {
AbstractNormalSchema schema = getNormalSchema(attributeMod.getSchema(), attrUtil.schemaClass());
AbstractAttr attr = null;
if (schema != null) {
attr = attributable.getAttr(schema.getName());
if (attr == null) {
attr = attrUtil.newAttr();
setAttrSchema(attributable, attr, schema);
if (attr.getSchema() == null) {
LOG.debug("Ignoring {} because no valid schema or template was found", attributeMod);
} else {
attr.setOwner(attributable);
attributable.addAttr(attr);
}
}
}
if (schema != null && attr != null && attr.getSchema() != null) {
if (attributable instanceof AbstractSubject) {
for (ExternalResource resource : resourceDAO.findAll()) {
for (AbstractMappingItem mapItem : attrUtil.
getMappingItems(resource, MappingPurpose.PROPAGATION)) {
if (schema.getName().equals(mapItem.getIntAttrName())
&& mapItem.getIntMappingType() == attrUtil.intMappingType()
&& ((AbstractSubject) attributable).getResources().contains(resource)) {
propByRes.add(ResourceOperation.UPDATE, resource.getName());
}
}
}
}
// 1.1 remove values
Set<Long> valuesToBeRemoved = new HashSet<Long>();
for (String valueToBeRemoved : attributeMod.getValuesToBeRemoved()) {
if (attr.getSchema().isUniqueConstraint()) {
if (attr.getUniqueValue() != null
&& valueToBeRemoved.equals(attr.getUniqueValue().getValueAsString())) {
valuesToBeRemoved.add(attr.getUniqueValue().getId());
}
} else {
for (AbstractAttrValue mav : attr.getValues()) {
if (valueToBeRemoved.equals(mav.getValueAsString())) {
valuesToBeRemoved.add(mav.getId());
}
}
}
}
for (Long attributeValueId : valuesToBeRemoved) {
attributeValueDAO.delete(attributeValueId, attrUtil.attrValueClass());
}
// 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()) {
attrDAO.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()) {
AbstractDerSchema derSchema = getDerSchema(derAttrToBeRemoved, attrUtil.derSchemaClass());
if (derSchema != null) {
AbstractDerAttr derAttr = attributable.getDerAttr(derSchema.getName());
if (derAttr == null) {
LOG.debug("No derived attribute found for schema {}", derSchema.getName());
} else {
derAttrDAO.delete(derAttr);
}
if (attributable instanceof AbstractSubject) {
for (ExternalResource resource : resourceDAO.findAll()) {
for (AbstractMappingItem mapItem : attrUtil.
getMappingItems(resource, MappingPurpose.PROPAGATION)) {
if (derSchema.getName().equals(mapItem.getIntAttrName())
&& mapItem.getIntMappingType() == attrUtil.derIntMappingType()
&& ((AbstractSubject) attributable).getResources().contains(resource)) {
propByRes.add(ResourceOperation.UPDATE, resource.getName());
if (mapItem.isAccountid() && derAttr != null
&& !derAttr.getValue(attributable.getAttrs()).isEmpty()) {
propByRes.addOldAccountId(resource.getName(),
derAttr.getValue(attributable.getAttrs()));
}
}
}
}
}
}
}
LOG.debug("Derived attributes to be removed:\n{}", propByRes);
// 6. derived attributes to be added
for (String derAttrToBeAdded : attributableMod.getDerAttrsToAdd()) {
AbstractDerSchema derSchema = getDerSchema(derAttrToBeAdded, attrUtil.derSchemaClass());
if (derSchema != null) {
if (attributable instanceof AbstractSubject) {
for (ExternalResource resource : resourceDAO.findAll()) {
for (AbstractMappingItem mapItem : attrUtil.
getMappingItems(resource, MappingPurpose.PROPAGATION)) {
if (derSchema.getName().equals(mapItem.getIntAttrName())
&& mapItem.getIntMappingType() == attrUtil.derIntMappingType()
&& ((AbstractSubject) attributable).getResources().contains(resource)) {
propByRes.add(ResourceOperation.UPDATE, resource.getName());
}
}
}
}
AbstractDerAttr 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()) {
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;
}
/**
* Add virtual attributes and specify values to be propagated.
*
* @param attributable attributable.
* @param vAttrs virtual attributes to be added.
* @param attrUtil attributable util.
*/
public void fillVirtual(final AbstractAttributable attributable, final Collection<AttributeTO> vAttrs,
final AttributableUtil attrUtil) {
for (AttributeTO attributeTO : vAttrs) {
AbstractVirAttr virAttr = attributable.getVirAttr(attributeTO.getSchema());
if (virAttr == null) {
AbstractVirSchema 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.setValues(attributeTO.getValues());
}
}
} else {
virAttr.setValues(attributeTO.getValues());
}
}
}
protected void fill(final AbstractAttributable attributable, final AbstractAttributableTO attributableTO,
final AttributableUtil attributableUtil, final SyncopeClientCompositeException scce) {
// 1. attributes
SyncopeClientException invalidValues = SyncopeClientException.build(ClientExceptionType.InvalidValues);
// Only consider attributeTO with values
for (AttributeTO attributeTO : attributableTO.getAttrs()) {
if (attributeTO.getValues() != null && !attributeTO.getValues().isEmpty()) {
AbstractNormalSchema schema = getNormalSchema(attributeTO.getSchema(), attributableUtil.schemaClass());
if (schema != null) {
AbstractAttr attr = attributable.getAttr(schema.getName());
if (attr == null) {
attr = attributableUtil.newAttr();
setAttrSchema(attributable, attr, schema);
}
if (attr.getSchema() == null) {
LOG.debug("Ignoring {} because no valid schema or template was found", attributeTO);
} else {
fillAttribute(attributeTO.getValues(), attributableUtil, schema, attr, invalidValues);
if (!attr.getValuesAsStrings().isEmpty()) {
attributable.addAttr(attr);
attr.setOwner(attributable);
}
}
}
}
}
if (!invalidValues.isEmpty()) {
scce.addException(invalidValues);
}
// 2. derived attributes
for (AttributeTO attributeTO : attributableTO.getDerAttrs()) {
AbstractDerSchema derSchema = getDerSchema(attributeTO.getSchema(), attributableUtil.derSchemaClass());
if (derSchema != null) {
AbstractDerAttr derAttr = attributableUtil.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 == attributableUtil.getType()
|| AttributableType.ROLE == attributableUtil.getType()) {
for (AttributeTO vattrTO : attributableTO.getVirAttrs()) {
AbstractVirSchema virSchema = getVirSchema(vattrTO.getSchema(), attributableUtil.virSchemaClass());
if (virSchema != null) {
AbstractVirAttr virAttr = attributableUtil.newVirAttr();
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);
}
}
}
}
fillVirtual(attributable, attributableTO.getVirAttrs(), attributableUtil);
// 4. resources
if (attributable instanceof AbstractSubject && attributableTO instanceof AbstractSubjectTO) {
for (String resourceName : ((AbstractSubjectTO) attributableTO).getResources()) {
ExternalResource resource = getResource(resourceName);
if (resource != null) {
((AbstractSubject) attributable).addResource(resource);
}
}
}
SyncopeClientException requiredValuesMissing = checkMandatory(attributableUtil, 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 AbstractAttr> attrs,
final Collection<? extends AbstractDerAttr> derAttrs,
final Collection<? extends AbstractVirAttr> virAttrs,
final Collection<ExternalResource> resources) {
AttributeTO attributeTO;
for (AbstractAttr attr : attrs) {
attributeTO = new AttributeTO();
attributeTO.setSchema(attr.getSchema().getName());
attributeTO.getValues().addAll(attr.getValuesAsStrings());
attributeTO.setReadonly(attr.getSchema().isReadonly());
attributableTO.getAttrs().add(attributeTO);
}
for (AbstractDerAttr derAttr : derAttrs) {
attributeTO = new AttributeTO();
attributeTO.setSchema(derAttr.getSchema().getName());
attributeTO.getValues().add(derAttr.getValue(attrs));
attributeTO.setReadonly(true);
attributableTO.getDerAttrs().add(attributeTO);
}
for (AbstractVirAttr virAttr : virAttrs) {
attributeTO = new AttributeTO();
attributeTO.setSchema(virAttr.getSchema().getName());
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.getName());
}
}
}
}