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()) {