[SYNCOPE-698] Implementation completed
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/72e6ceb0 Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/72e6ceb0 Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/72e6ceb0 Branch: refs/heads/master Commit: 72e6ceb0624a90a9b63bafce37c04104c7391d79 Parents: 5537d29 Author: Francesco Chicchiriccò <[email protected]> Authored: Fri Sep 25 16:43:23 2015 +0200 Committer: Francesco Chicchiriccò <[email protected]> Committed: Fri Sep 25 16:43:23 2015 +0200 ---------------------------------------------------------------------- .../syncope/common/lib/to/MappingItemTO.java | 11 +- .../apache/syncope/common/lib/to/SyncopeTO.java | 9 + .../syncope/core/logic/ResourceLogic.java | 8 +- .../apache/syncope/core/logic/SyncopeLogic.java | 25 +- .../init/ClassPathScanImplementationLookup.java | 6 + .../syncope/core/misc/ConnObjectUtils.java | 232 ++------- .../apache/syncope/core/misc/MappingUtils.java | 501 +++++++++++++------ .../core/misc/security/AuthDataAccessor.java | 9 +- .../serialization/GuardedStringSerializer.java | 4 +- .../persistence/api/ImplementationLookup.java | 1 + .../api/entity/resource/MappingItem.java | 3 + .../jpa/entity/resource/JPAMappingItem.java | 22 + .../entity/ExternalResourceValidator.java | 22 + .../api/data/MappingItemTransformer.java | 47 ++ .../provisioning/java/AsyncConnectorFacade.java | 14 +- .../provisioning/java/VirAttrHandlerImpl.java | 44 +- .../java/data/AbstractAnyDataBinder.java | 19 +- .../data/DefaultMappingItemTransformer.java | 40 ++ .../AbstractPropagationTaskExecutor.java | 33 +- .../propagation/DefaultPropagationActions.java | 2 +- .../propagation/DefaultPropagationReporter.java | 2 +- .../propagation/PropagationManagerImpl.java | 10 +- .../java/sync/AbstractPushResultHandler.java | 6 +- .../java/sync/AbstractSyncResultHandler.java | 2 +- .../sync/PlainAttrsSyncCorrelationRule.java | 41 +- .../core/provisioning/java/sync/SyncUtils.java | 37 +- .../reference/PrefixMappingItemTransformer.java | 55 ++ .../fit/core/reference/SyncTaskITCase.java | 101 ++-- 28 files changed, 824 insertions(+), 482 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/common/lib/src/main/java/org/apache/syncope/common/lib/to/MappingItemTO.java ---------------------------------------------------------------------- diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/MappingItemTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/MappingItemTO.java index f252fbb..b838f6e 100644 --- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/MappingItemTO.java +++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/MappingItemTO.java @@ -18,6 +18,8 @@ */ package org.apache.syncope.common.lib.to; +import java.util.ArrayList; +import java.util.List; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; import org.apache.syncope.common.lib.AbstractBaseBean; @@ -64,10 +66,12 @@ public class MappingItemTO extends AbstractBaseBean { private String mandatoryCondition = "false"; /** - * Mapping purposes: SYNCHRONIZATION, PROPAGATION, BOTH, NONE. + * Mapping purposes. */ private MappingPurpose purpose; + private final List<String> mappingItemTransformerClassNames = new ArrayList<>(); + public boolean isConnObjectKey() { return connObjectKey; } @@ -131,4 +135,9 @@ public class MappingItemTO extends AbstractBaseBean { public void setPurpose(final MappingPurpose purpose) { this.purpose = purpose; } + + public List<String> getMappingItemTransformerClassNames() { + return mappingItemTransformerClassNames; + } + } http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java ---------------------------------------------------------------------- diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java index 638f32f..f65badc 100644 --- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java +++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java @@ -65,6 +65,8 @@ public class SyncopeTO extends AbstractBaseBean { private final List<String> passwordRules = new ArrayList<>(); + private final List<String> mappingItemTransformers = new ArrayList<>(); + private final List<String> taskJobs = new ArrayList<>(); private final List<String> logicActions = new ArrayList<>(); @@ -163,6 +165,13 @@ public class SyncopeTO extends AbstractBaseBean { return passwordRules; } + @XmlElementWrapper(name = "mappingItemTransformers") + @XmlElement(name = "mappingItemTransformer") + @JsonProperty("mappingItemTransformers") + public List<String> getMappingItemTransformers() { + return mappingItemTransformers; + } + @XmlElementWrapper(name = "taskJobs") @XmlElement(name = "taskJob") @JsonProperty("taskJobs") http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java ---------------------------------------------------------------------- diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java index 7defb27..c3ce0c6 100644 --- a/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java +++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java @@ -36,7 +36,6 @@ import org.apache.syncope.common.lib.to.ResourceTO; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.common.lib.types.Entitlement; -import org.apache.syncope.common.lib.types.MappingPurpose; import org.apache.syncope.core.persistence.api.dao.DuplicateException; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; import org.apache.syncope.core.persistence.api.dao.NotFoundException; @@ -93,6 +92,9 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> { private ConnObjectUtils connObjectUtils; @Autowired + private MappingUtils mappingUtils; + + @Autowired private ConnectorFactory connFactory; @PreAuthorize("hasRole('" + Entitlement.RESOURCE_CREATE + "')") @@ -216,13 +218,13 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> { throw new NotFoundException( "ConnObjectKey mapping for " + init.getMiddle() + " " + anyKey + " on resource '" + key + "'"); } - String connObjectKeyValue = MappingUtils.getConnObjectKeyValue(any, init.getRight()); + String connObjectKeyValue = mappingUtils.getConnObjectKeyValue(any, init.getRight()); Connector connector = connFactory.getConnector(init.getLeft()); ConnectorObject connectorObject = connector.getObject( init.getRight().getObjectClass(), new Uid(connObjectKeyValue), - connector.getOperationOptions(MappingUtils.getMappingItems(init.getRight(), MappingPurpose.BOTH))); + connector.getOperationOptions(MappingUtils.getBothMappingItems(init.getRight()))); if (connectorObject == null) { throw new NotFoundException( "Object " + connObjectKeyValue + " with class " + init.getRight().getObjectClass() http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java ---------------------------------------------------------------------- diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java index 7f9b7c9..480efc1 100644 --- a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java +++ b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java @@ -82,7 +82,7 @@ public class SyncopeLogic extends AbstractLogic<SyncopeTO> { private PasswordGenerator passwordGenerator; @Autowired - private ImplementationLookup implementationLookup; + private ImplementationLookup implLookup; @Resource(name = "velocityResourceLoader") private ResourceWithFallbackLoader resourceLoader; @@ -128,17 +128,18 @@ public class SyncopeLogic extends AbstractLogic<SyncopeTO> { syncopeTO.setVirAttrCache(virAttrCache.getClass().getName()); syncopeTO.setPasswordGenerator(passwordGenerator.getClass().getName()); - syncopeTO.getReportlets().addAll(implementationLookup.getClassNames(Type.REPORTLET)); - syncopeTO.getAccountRules().addAll(implementationLookup.getClassNames(Type.ACCOUNT_RULE)); - syncopeTO.getPasswordRules().addAll(implementationLookup.getClassNames(Type.PASSWORD_RULE)); - syncopeTO.getTaskJobs().addAll(implementationLookup.getClassNames(Type.TASKJOBDELEGATE)); - syncopeTO.getLogicActions().addAll(implementationLookup.getClassNames(Type.LOGIC_ACTIONS)); - syncopeTO.getPropagationActions().addAll(implementationLookup.getClassNames(Type.PROPAGATION_ACTIONS)); - syncopeTO.getSyncActions().addAll(implementationLookup.getClassNames(Type.SYNC_ACTIONS)); - syncopeTO.getPushActions().addAll(implementationLookup.getClassNames(Type.PUSH_ACTIONS)); - syncopeTO.getSyncCorrelationRules().addAll(implementationLookup.getClassNames(Type.SYNC_CORRELATION_RULE)); - syncopeTO.getPushCorrelationRules().addAll(implementationLookup.getClassNames(Type.PUSH_CORRELATION_RULE)); - syncopeTO.getValidators().addAll(implementationLookup.getClassNames(Type.VALIDATOR)); + syncopeTO.getReportlets().addAll(implLookup.getClassNames(Type.REPORTLET)); + syncopeTO.getAccountRules().addAll(implLookup.getClassNames(Type.ACCOUNT_RULE)); + syncopeTO.getPasswordRules().addAll(implLookup.getClassNames(Type.PASSWORD_RULE)); + syncopeTO.getMappingItemTransformers().addAll(implLookup.getClassNames(Type.MAPPING_ITEM_TRANSFORMER)); + syncopeTO.getTaskJobs().addAll(implLookup.getClassNames(Type.TASKJOBDELEGATE)); + syncopeTO.getLogicActions().addAll(implLookup.getClassNames(Type.LOGIC_ACTIONS)); + syncopeTO.getPropagationActions().addAll(implLookup.getClassNames(Type.PROPAGATION_ACTIONS)); + syncopeTO.getSyncActions().addAll(implLookup.getClassNames(Type.SYNC_ACTIONS)); + syncopeTO.getPushActions().addAll(implLookup.getClassNames(Type.PUSH_ACTIONS)); + syncopeTO.getSyncCorrelationRules().addAll(implLookup.getClassNames(Type.SYNC_CORRELATION_RULE)); + syncopeTO.getPushCorrelationRules().addAll(implLookup.getClassNames(Type.PUSH_CORRELATION_RULE)); + syncopeTO.getValidators().addAll(implLookup.getClassNames(Type.VALIDATOR)); Set<String> htmlTemplates = new HashSet<>(); Set<String> textTemplates = new HashSet<>(); http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java ---------------------------------------------------------------------- diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java index d68df96..5147a39 100644 --- a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java +++ b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java @@ -38,6 +38,7 @@ import org.apache.syncope.core.persistence.api.dao.AccountRuleConfClass; import org.apache.syncope.core.persistence.api.dao.PasswordRule; import org.apache.syncope.core.persistence.api.dao.PasswordRuleConfClass; import org.apache.syncope.core.provisioning.api.LogicActions; +import org.apache.syncope.core.provisioning.api.data.MappingItemTransformer; import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate; import org.apache.syncope.core.provisioning.api.propagation.PropagationActions; import org.apache.syncope.core.provisioning.api.sync.PushActions; @@ -90,6 +91,7 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup { scanner.addIncludeFilter(new AssignableTypeFilter(Reportlet.class)); scanner.addIncludeFilter(new AssignableTypeFilter(AccountRule.class)); scanner.addIncludeFilter(new AssignableTypeFilter(PasswordRule.class)); + scanner.addIncludeFilter(new AssignableTypeFilter(MappingItemTransformer.class)); scanner.addIncludeFilter(new AssignableTypeFilter(SchedTaskJobDelegate.class)); scanner.addIncludeFilter(new AssignableTypeFilter(LogicActions.class)); scanner.addIncludeFilter(new AssignableTypeFilter(PropagationActions.class)); @@ -131,6 +133,10 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup { } } + if (MappingItemTransformer.class.isAssignableFrom(clazz) && !isAbsractClazz) { + classNames.get(Type.MAPPING_ITEM_TRANSFORMER).add(clazz.getName()); + } + if (SchedTaskJobDelegate.class.isAssignableFrom(clazz) && !isAbsractClazz && !SyncJobDelegate.class.isAssignableFrom(clazz) && !PushJobDelegate.class.isAssignableFrom(clazz)) { http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java ---------------------------------------------------------------------- diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java b/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java index 7c39bb7..f78cc31 100644 --- a/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java +++ b/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java @@ -21,11 +21,7 @@ package org.apache.syncope.core.misc; import org.apache.syncope.core.misc.policy.InvalidPasswordRuleConf; import org.apache.syncope.core.misc.security.SecureRandomUtils; import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.AnyOperations; import org.apache.syncope.common.lib.patch.AnyPatch; @@ -37,17 +33,11 @@ import org.apache.syncope.common.lib.to.ConnObjectTO; import org.apache.syncope.common.lib.to.GroupTO; import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.AnyTypeKind; -import org.apache.syncope.common.lib.types.AttrSchemaType; -import org.apache.syncope.common.lib.types.MappingPurpose; -import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; -import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtils; import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource; import org.apache.syncope.core.persistence.api.entity.resource.MappingItem; -import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; -import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.task.SyncTask; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.misc.security.Encryptor; @@ -71,6 +61,8 @@ public class ConnObjectUtils { private static final Logger LOG = LoggerFactory.getLogger(ConnObjectUtils.class); + private static final Encryptor ENCRYPTOR = Encryptor.getInstance(); + @Autowired private TemplateUtils templateUtils; @@ -84,12 +76,44 @@ public class ConnObjectUtils { private ExternalResourceDAO resourceDAO; @Autowired - private PlainSchemaDAO plainSchemaDAO; + private PasswordGenerator passwordGenerator; @Autowired - private PasswordGenerator pwdGen; + private MappingUtils mappingUtils; + + /** + * Extract password value from passed value (if instance of GuardedString or GuardedByteArray). + * + * @param pwd received from the underlying connector + * @return password value + */ + public static String getPassword(final Object pwd) { + final StringBuilder result = new StringBuilder(); + + if (pwd instanceof GuardedString) { + ((GuardedString) pwd).access(new GuardedString.Accessor() { + + @Override + public void access(final char[] clearChars) { + result.append(clearChars); + } + }); + } else if (pwd instanceof GuardedByteArray) { + ((GuardedByteArray) pwd).access(new GuardedByteArray.Accessor() { + + @Override + public void access(final byte[] clearBytes) { + result.append(new String(clearBytes)); + } + }); + } else if (pwd instanceof String) { + result.append((String) pwd); + } else { + result.append(pwd.toString()); + } - private final Encryptor encryptor = Encryptor.getInstance(); + return result.toString(); + } /** * Build a UserTO / GroupTO / AnyObjectTO out of connector object attributes and schema mapping. @@ -131,7 +155,7 @@ public class ConnObjectUtils { String password; try { - password = pwdGen.generate(ruleConfs); + password = passwordGenerator.generate(ruleConfs); } catch (InvalidPasswordRuleConf e) { LOG.error("Could not generate policy-compliant random password for {}", userTO, e); @@ -167,7 +191,7 @@ public class ConnObjectUtils { // update password if and only if password is really changed User user = userDAO.authFind(key); if (StringUtils.isBlank(((UserTO) updated).getPassword()) - || encryptor.verify(((UserTO) updated).getPassword(), + || ENCRYPTOR.verify(((UserTO) updated).getPassword(), user.getCipherAlgorithm(), user.getPassword())) { ((UserTO) updated).setPassword(null); @@ -190,127 +214,8 @@ public class ConnObjectUtils { // 1. fill with data from connector object anyTO.setRealm(syncTask.getDestinatioRealm().getFullPath()); - for (MappingItem item : MappingUtils.getMappingItems(provision, MappingPurpose.SYNCHRONIZATION)) { - Attribute attr = obj.getAttributeByName(item.getExtAttrName()); - - AttrTO attrTO; - switch (item.getIntMappingType()) { - case UserKey: - case GroupKey: - case AnyObjectKey: - break; - - case Password: - if (anyTO instanceof UserTO && attr != null && attr.getValue() != null - && !attr.getValue().isEmpty()) { - - ((UserTO) anyTO).setPassword(getPassword(attr.getValue().get(0))); - } - break; - - case Username: - if (anyTO instanceof UserTO) { - ((UserTO) anyTO).setUsername(attr == null || attr.getValue().isEmpty() - || attr.getValue().get(0) == null - ? null - : attr.getValue().get(0).toString()); - } - break; - - case GroupName: - if (anyTO instanceof GroupTO) { - ((GroupTO) anyTO).setName(attr == null || attr.getValue().isEmpty() - || attr.getValue().get(0) == null - ? null - : attr.getValue().get(0).toString()); - } - break; - - case GroupOwnerSchema: - if (anyTO instanceof GroupTO && attr != null) { - // using a special attribute (with schema "", that will be ignored) for carrying the - // GroupOwnerSchema value - attrTO = new AttrTO(); - attrTO.setSchema(StringUtils.EMPTY); - if (attr.getValue().isEmpty() || attr.getValue().get(0) == null) { - attrTO.getValues().add(StringUtils.EMPTY); - } else { - attrTO.getValues().add(attr.getValue().get(0).toString()); - } - - ((GroupTO) anyTO).getPlainAttrs().add(attrTO); - } - break; - - case UserPlainSchema: - case GroupPlainSchema: - case AnyObjectPlainSchema: - attrTO = new AttrTO(); - attrTO.setSchema(item.getIntAttrName()); - - PlainSchema schema = plainSchemaDAO.find(item.getIntAttrName()); - - for (Object value : attr == null || attr.getValue() == null - ? Collections.emptyList() - : attr.getValue()) { - - AttrSchemaType schemaType = schema == null ? AttrSchemaType.String : schema.getType(); - if (value != null) { - final PlainAttrValue attrValue = anyUtils.newPlainAttrValue(); - switch (schemaType) { - case String: - attrValue.setStringValue(value.toString()); - break; - - case Binary: - attrValue.setBinaryValue((byte[]) value); - break; - - default: - try { - attrValue.parseValue(schema, value.toString()); - } catch (ParsingValidationException e) { - LOG.error("While parsing provided value {}", value, e); - attrValue.setStringValue(value.toString()); - schemaType = AttrSchemaType.String; - } - break; - } - attrTO.getValues().add(attrValue.getValueAsString(schemaType)); - } - } - - anyTO.getPlainAttrs().add(attrTO); - break; - - case UserDerivedSchema: - case GroupDerivedSchema: - case AnyObjectDerivedSchema: - attrTO = new AttrTO(); - attrTO.setSchema(item.getIntAttrName()); - anyTO.getDerAttrs().add(attrTO); - break; - - case UserVirtualSchema: - case GroupVirtualSchema: - case AnyObjectVirtualSchema: - attrTO = new AttrTO(); - attrTO.setSchema(item.getIntAttrName()); - - for (Object value : attr == null || attr.getValue() == null - ? Collections.emptyList() - : attr.getValue()) { - - if (value != null) { - attrTO.getValues().add(value.toString()); - } - } - - anyTO.getVirAttrs().add(attrTO); - break; - - default: - } + for (MappingItem item : MappingUtils.getSyncMappingItems(provision)) { + mappingUtils.setIntValues(item, obj.getAttributeByName(item.getExtAttrName()), anyTO, anyUtils); } // 2. add data from defined template (if any) @@ -320,40 +225,6 @@ public class ConnObjectUtils { } /** - * Extract password value from passed value (if instance of GuardedString or GuardedByteArray). - * - * @param pwd received from the underlying connector - * @return password value - */ - public String getPassword(final Object pwd) { - final StringBuilder result = new StringBuilder(); - - if (pwd instanceof GuardedString) { - ((GuardedString) pwd).access(new GuardedString.Accessor() { - - @Override - public void access(final char[] clearChars) { - result.append(clearChars); - } - }); - } else if (pwd instanceof GuardedByteArray) { - ((GuardedByteArray) pwd).access(new GuardedByteArray.Accessor() { - - @Override - public void access(final byte[] clearBytes) { - result.append(new String(clearBytes)); - } - }); - } else if (pwd instanceof String) { - result.append((String) pwd); - } else { - result.append(pwd.toString()); - } - - return result.toString(); - } - - /** * Get connector object TO from a connector object. * * @param connObject connector object. @@ -385,25 +256,4 @@ public class ConnObjectUtils { return connObjectTO; } - - /** - * Transform a - * <code>Collection</code> of {@link Attribute} instances into a {@link Map}. The key to each element in the map is - * the <i>name</i> of an - * <code>Attribute</code>. The value of each element in the map is the - * <code>Attribute</code> instance with that name. <br/> Different from the original because: <ul> <li>map keys are - * transformed toUpperCase()</li> <li>returned map is mutable</li> </ul> - * - * @param attributes set of attribute to transform to a map. - * @return a map of string and attribute. - * - * @see org.identityconnectors.framework.common.objects.AttributeUtil#toMap(java.util.Collection) - */ - public Map<String, Attribute> toMap(final Collection<? extends Attribute> attributes) { - final Map<String, Attribute> map = new HashMap<>(); - for (Attribute attr : attributes) { - map.put(attr.getName().toUpperCase(), attr); - } - return map; - } } http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/misc/src/main/java/org/apache/syncope/core/misc/MappingUtils.java ---------------------------------------------------------------------- diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/MappingUtils.java b/core/misc/src/main/java/org/apache/syncope/core/misc/MappingUtils.java index 2f0a0c5..085f07d 100644 --- a/core/misc/src/main/java/org/apache/syncope/core/misc/MappingUtils.java +++ b/core/misc/src/main/java/org/apache/syncope/core/misc/MappingUtils.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.misc; -import org.apache.syncope.core.misc.policy.InvalidPasswordRuleConf; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -27,16 +26,24 @@ import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.ListUtils; import org.apache.commons.collections4.Predicate; import org.apache.commons.jexl2.JexlContext; import org.apache.commons.jexl2.MapContext; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.SerializationUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.patch.AttrPatch; +import org.apache.syncope.common.lib.to.AnyTO; +import org.apache.syncope.common.lib.to.AttrTO; +import org.apache.syncope.common.lib.to.GroupTO; +import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.AttrSchemaType; import org.apache.syncope.common.lib.types.IntMappingType; import org.apache.syncope.common.lib.types.MappingPurpose; +import org.apache.syncope.core.misc.policy.InvalidPasswordRuleConf; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtils; @@ -53,17 +60,21 @@ import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrValue; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.provisioning.api.cache.VirAttrCache; import org.apache.syncope.core.misc.security.Encryptor; -import org.apache.syncope.core.misc.spring.ApplicationContextProvider; import org.apache.syncope.core.misc.jexl.JexlUtils; import org.apache.syncope.core.misc.security.PasswordGenerator; +import org.apache.syncope.core.misc.spring.ApplicationContextProvider; +import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.Schema; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; import org.apache.syncope.core.persistence.api.entity.resource.Mapping; import org.apache.syncope.core.persistence.api.entity.resource.Provision; import org.apache.syncope.core.provisioning.api.VirAttrHandler; +import org.apache.syncope.core.provisioning.api.data.MappingItemTransformer; import org.identityconnectors.framework.common.FrameworkUtil; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeBuilder; @@ -72,14 +83,45 @@ import org.identityconnectors.framework.common.objects.Name; import org.identityconnectors.framework.common.objects.OperationalAttributes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; -public final class MappingUtils { +@Component +public class MappingUtils { private static final Logger LOG = LoggerFactory.getLogger(MappingUtils.class); private static final Encryptor ENCRYPTOR = Encryptor.getInstance(); + @Autowired + private UserDAO userDAO; + + @Autowired + private AnyTypeDAO anyTypeDAO; + + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Autowired + private VirSchemaDAO virSchemaDAO; + + @Autowired + private VirAttrHandler virAttrHandler; + + @Autowired + private VirAttrCache virAttrCache; + + @Autowired + private PasswordGenerator passwordGenerator; + + @Autowired + private EntityFactory entityFactory; + + @Autowired + private AnyUtilsFactory anyUtilsFactory; + public static <T extends MappingItem> Collection<T> getMatchingMappingItems( final Collection<T> items, final IntMappingType type) { @@ -116,6 +158,144 @@ public final class MappingUtils { }); } + public static MappingItem getConnObjectKeyItem(final Provision provision) { + Mapping mapping = null; + if (provision != null) { + mapping = provision.getMapping(); + } + + return mapping == null + ? null + : mapping.getConnObjectKeyItem(); + } + + private static List<MappingItem> getMappingItems(final Provision provision, final MappingPurpose purpose) { + List<? extends MappingItem> items = Collections.<MappingItem>emptyList(); + if (provision != null) { + items = provision.getMapping().getItems(); + } + + List<MappingItem> result = new ArrayList<>(); + + switch (purpose) { + case SYNCHRONIZATION: + for (MappingItem item : items) { + if (MappingPurpose.PROPAGATION != item.getPurpose() + && MappingPurpose.NONE != item.getPurpose()) { + + result.add(item); + } + } + break; + + case PROPAGATION: + for (MappingItem item : items) { + if (MappingPurpose.SYNCHRONIZATION != item.getPurpose() + && MappingPurpose.NONE != item.getPurpose()) { + + result.add(item); + } + } + break; + + case BOTH: + for (MappingItem item : items) { + if (MappingPurpose.NONE != item.getPurpose()) { + result.add(item); + } + } + break; + + case NONE: + for (MappingItem item : items) { + if (MappingPurpose.NONE == item.getPurpose()) { + result.add(item); + } + } + break; + + default: + } + + return result; + } + + public static List<MappingItem> getBothMappingItems(final Provision provision) { + return getMappingItems(provision, MappingPurpose.BOTH); + } + + public static List<MappingItem> getPropagationMappingItems(final Provision provision) { + return getMappingItems(provision, MappingPurpose.PROPAGATION); + } + + public static List<MappingItem> getSyncMappingItems(final Provision provision) { + return getMappingItems(provision, MappingPurpose.SYNCHRONIZATION); + } + + /** + * Build __NAME__ for propagation. First look if there ia a defined connObjectLink for the given resource (and in + * this case evaluate as JEXL); otherwise, take given connObjectKey. + * + * @param any given any object + * @param provision external resource + * @param connObjectKey connector object key + * @return the value to be propagated as __NAME__ + */ + public static Name evaluateNAME(final Any<?, ?, ?> any, final Provision provision, final String connObjectKey) { + if (StringUtils.isBlank(connObjectKey)) { + // LOG error but avoid to throw exception: leave it to the external resource + LOG.error("Missing ConnObjectKey for '{}': ", provision.getResource()); + } + + // Evaluate connObjectKey expression + String connObjectLink = provision == null || provision.getMapping() == null + ? null + : provision.getMapping().getConnObjectLink(); + String evalConnObjectLink = null; + if (StringUtils.isNotBlank(connObjectLink)) { + JexlContext jexlContext = new MapContext(); + JexlUtils.addFieldsToContext(any, jexlContext); + JexlUtils.addPlainAttrsToContext(any.getPlainAttrs(), jexlContext); + JexlUtils.addDerAttrsToContext(any.getDerAttrs(), any.getPlainAttrs(), jexlContext); + evalConnObjectLink = JexlUtils.evaluate(connObjectLink, jexlContext); + } + + // If connObjectLink evaluates to an empty string, just use the provided connObjectKey as Name(), + // otherwise evaluated connObjectLink expression is taken as Name(). + Name name; + if (StringUtils.isBlank(evalConnObjectLink)) { + // add connObjectKey as __NAME__ attribute ... + LOG.debug("Add connObjectKey [{}] as __NAME__", connObjectKey); + name = new Name(connObjectKey); + } else { + LOG.debug("Add connObjectLink [{}] as __NAME__", evalConnObjectLink); + name = new Name(evalConnObjectLink); + + // connObjectKey not propagated: it will be used to set the value for __UID__ attribute + LOG.debug("connObjectKey will be used just as __UID__ attribute"); + } + + return name; + } + + public static List<MappingItemTransformer> getMappingItemTransformers(final MappingItem mappingItem) { + List<MappingItemTransformer> result = new ArrayList<>(); + + for (String className : mappingItem.getMappingItemTransformerClassNames()) { + try { + Class<?> transformerClass = ClassUtils.getClass(className); + + result.add((MappingItemTransformer) ApplicationContextProvider. + getBeanFactory(). + createBean(transformerClass, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false)); + } catch (Exception e) { + LOG.error("Could not instantiate {}, ignoring...", className, e); + } + } + + return result; + } + /** * Prepare attributes for sending to a connector instance. * @@ -127,7 +307,8 @@ public final class MappingUtils { * @param provision provision information * @return connObjectLink + prepared attributes */ - public static Pair<String, Set<Attribute>> prepareAttrs( + @Transactional(readOnly = true) + public Pair<String, Set<Attribute>> prepareAttrs( final Any<?, ?, ?> any, final String password, final boolean changePwd, @@ -138,10 +319,6 @@ public final class MappingUtils { LOG.debug("Preparing resource attributes for {} with provision {} for attributes {}", any, provision, any.getPlainAttrs()); - DefaultListableBeanFactory beanFactory = ApplicationContextProvider.getBeanFactory(); - VirAttrCache virAttrCache = beanFactory.getBean(VirAttrCache.class); - PasswordGenerator passwordGenerator = beanFactory.getBean(PasswordGenerator.class); - Set<Attribute> attributes = new HashSet<>(); String connObjectKey = null; @@ -157,8 +334,7 @@ public final class MappingUtils { virAttrCache.expire(any.getType().getKey(), any.getKey(), mapping.getIntAttrName()); } - Pair<String, Attribute> preparedAttr = prepareAttr( - provision, mapping, any, password, passwordGenerator, vAttrs); + Pair<String, Attribute> preparedAttr = prepareAttr(provision, mapping, any, password, vAttrs); if (preparedAttr != null && preparedAttr.getKey() != null) { connObjectKey = preparedAttr.getKey(); @@ -211,22 +387,16 @@ public final class MappingUtils { * @param mapItem mapping item for the given attribute * @param any any object * @param password clear-text password - * @param passwordGenerator password generator * @param vAttrs virtual attributes to be managed * @return connObjectLink + prepared attribute */ - @SuppressWarnings("unchecked") - private static Pair<String, Attribute> prepareAttr( + private Pair<String, Attribute> prepareAttr( final Provision provision, final MappingItem mapItem, - final Any<?, ?, ?> any, final String password, final PasswordGenerator passwordGenerator, + final Any<?, ?, ?> any, final String password, final Map<String, AttrPatch> vAttrs) { List<Any<?, ?, ?>> anys = new ArrayList<>(); - DefaultListableBeanFactory beanFactory = ApplicationContextProvider.getBeanFactory(); - AnyUtilsFactory anyUtilsFactory = beanFactory.getBean(AnyUtilsFactory.class); - VirAttrHandler virAttrHandler = beanFactory.getBean(VirAttrHandler.class); - switch (mapItem.getIntMappingType().getAnyTypeKind()) { case USER: if (any instanceof User) { @@ -236,7 +406,6 @@ public final class MappingUtils { case GROUP: if (any instanceof User) { - UserDAO userDAO = beanFactory.getBean(UserDAO.class); for (Group group : userDAO.findAllGroups((User) any)) { virAttrHandler.retrieveVirAttrValues(group); anys.add(group); @@ -266,7 +435,6 @@ public final class MappingUtils { case UserPlainSchema: case GroupPlainSchema: case AnyObjectPlainSchema: - PlainSchemaDAO plainSchemaDAO = beanFactory.getBean(PlainSchemaDAO.class); schema = plainSchemaDAO.find(mapItem.getIntAttrName()); schemaType = schema == null ? AttrSchemaType.String : schema.getType(); break; @@ -274,7 +442,6 @@ public final class MappingUtils { case UserVirtualSchema: case GroupVirtualSchema: case AnyObjectVirtualSchema: - VirSchemaDAO virSchemaDAO = beanFactory.getBean(VirSchemaDAO.class); schema = virSchemaDAO.find(mapItem.getIntAttrName()); readOnlyVirSchema = (schema != null && schema.isReadonly()); schemaType = AttrSchemaType.String; @@ -357,63 +524,16 @@ public final class MappingUtils { return result; } - /** - * Build __NAME__ for propagation. First look if there ia a defined connObjectLink for the given resource (and in - * this case evaluate as JEXL); otherwise, take given connObjectKey. - * - * @param any given any object - * @param provision external resource - * @param connObjectKey connector object key - * @return the value to be propagated as __NAME__ - */ - public static Name evaluateNAME(final Any<?, ?, ?> any, final Provision provision, final String connObjectKey) { - if (StringUtils.isBlank(connObjectKey)) { - // LOG error but avoid to throw exception: leave it to the external resource - LOG.error("Missing ConnObjectKey for '{}': ", provision.getResource()); - } - - // Evaluate connObjectKey expression - String connObjectLink = provision == null || provision.getMapping() == null - ? null - : provision.getMapping().getConnObjectLink(); - String evalConnObjectLink = null; - if (StringUtils.isNotBlank(connObjectLink)) { - JexlContext jexlContext = new MapContext(); - JexlUtils.addFieldsToContext(any, jexlContext); - JexlUtils.addPlainAttrsToContext(any.getPlainAttrs(), jexlContext); - JexlUtils.addDerAttrsToContext(any.getDerAttrs(), any.getPlainAttrs(), jexlContext); - evalConnObjectLink = JexlUtils.evaluate(connObjectLink, jexlContext); - } - - // If connObjectLink evaluates to an empty string, just use the provided connObjectKey as Name(), - // otherwise evaluated connObjectLink expression is taken as Name(). - Name name; - if (StringUtils.isBlank(evalConnObjectLink)) { - // add connObjectKey as __NAME__ attribute ... - LOG.debug("Add connObjectKey [{}] as __NAME__", connObjectKey); - name = new Name(connObjectKey); - } else { - LOG.debug("Add connObjectLink [{}] as __NAME__", evalConnObjectLink); - name = new Name(evalConnObjectLink); - - // connObjectKey not propagated: it will be used to set the value for __UID__ attribute - LOG.debug("connObjectKey will be used just as __UID__ attribute"); - } - - return name; - } - - private static String getGroupOwnerValue(final Provision provision, final Any<?, ?, ?> any) { - Pair<String, Attribute> preparedAttr = prepareAttr(provision, getConnObjectKeyItem(provision), - any, null, null, Collections.<String, AttrPatch>emptyMap()); + private String getGroupOwnerValue(final Provision provision, final Any<?, ?, ?> any) { + Pair<String, Attribute> preparedAttr = prepareAttr( + provision, getConnObjectKeyItem(provision), any, null, Collections.<String, AttrPatch>emptyMap()); String connObjectKey = preparedAttr.getKey(); - final Name groupOwnerName = evaluateNAME(any, provision, connObjectKey); - return groupOwnerName.getNameValue(); + return evaluateNAME(any, provision, connObjectKey).getNameValue(); } /** - * Get attribute values. + * Get attribute values for the given {@link MappingItem} and any objects. * * @param provision provision information * @param mappingItem mapping item @@ -421,17 +541,15 @@ public final class MappingUtils { * @param vAttrs virtual attributes to be managed * @return attribute values. */ - public static List<PlainAttrValue> getIntValues(final Provision provision, + @Transactional(readOnly = true) + public List<PlainAttrValue> getIntValues(final Provision provision, final MappingItem mappingItem, final List<Any<?, ?, ?>> anys, final Map<String, AttrPatch> vAttrs) { LOG.debug("Get attributes for '{}' and mapping type '{}'", anys, mappingItem.getIntMappingType()); - EntityFactory entityFactory = - ApplicationContextProvider.getBeanFactory().getBean(EntityFactory.class); - AnyUtilsFactory anyUtilsFactory = - ApplicationContextProvider.getBeanFactory().getBean(AnyUtilsFactory.class); + boolean transform = true; + List<PlainAttrValue> values = new ArrayList<>(); - PlainAttrValue attrValue; switch (mappingItem.getIntMappingType()) { case UserPlainSchema: case GroupPlainSchema: @@ -440,9 +558,15 @@ public final class MappingUtils { PlainAttr<?> attr = any.getPlainAttr(mappingItem.getIntAttrName()); if (attr != null) { if (attr.getUniqueValue() != null) { - values.add(attr.getUniqueValue()); + PlainAttrUniqueValue value = SerializationUtils.clone(attr.getUniqueValue()); + value.setAttr(null); + values.add(value); } else if (attr.getValues() != null) { - values.addAll(attr.getValues()); + for (PlainAttrValue value : attr.getValues()) { + PlainAttrValue shadow = SerializationUtils.clone(value); + shadow.setAttr(null); + values.add(shadow); + } } } @@ -458,26 +582,27 @@ public final class MappingUtils { case UserVirtualSchema: case GroupVirtualSchema: case AnyObjectVirtualSchema: + // virtual attributes don't get transformed + transform = false; + for (Any<?, ?, ?> any : anys) { AnyUtils anyUtils = anyUtilsFactory.getInstance(any); - VirAttr<?> virAttr = any.getVirAttr(mappingItem.getIntAttrName()); - if (virAttr != null) { + VirAttr<?> attr = any.getVirAttr(mappingItem.getIntAttrName()); + if (attr != null) { if (vAttrs != null) { if (vAttrs.containsKey(mappingItem.getIntAttrName())) { - virAttr.getValues().clear(); - virAttr.getValues().addAll( + attr.getValues().clear(); + attr.getValues().addAll( vAttrs.get(mappingItem.getIntAttrName()).getAttrTO().getValues()); } else { throw new IllegalArgumentException("Don't need to update virtual attribute '" + mappingItem.getIntAttrName() + "'"); } } - if (virAttr.getValues() != null) { - for (String value : virAttr.getValues()) { - attrValue = anyUtils.newPlainAttrValue(); - attrValue.setStringValue(value); - values.add(attrValue); - } + for (String value : attr.getValues()) { + PlainAttrValue attrValue = anyUtils.newPlainAttrValue(); + attrValue.setStringValue(value); + values.add(attrValue); } } @@ -486,7 +611,7 @@ public final class MappingUtils { + "\n* IntMappingType {}" + "\n* Attribute values {}", any.getClass().getSimpleName(), - virAttr, mappingItem.getIntAttrName(), mappingItem.getIntMappingType(), values); + attr, mappingItem.getIntAttrName(), mappingItem.getIntMappingType(), values); } break; @@ -495,10 +620,10 @@ public final class MappingUtils { case AnyObjectDerivedSchema: for (Any<?, ?, ?> any : anys) { AnyUtils anyUtils = anyUtilsFactory.getInstance(any); - DerAttr<?> derAttr = any.getDerAttr(mappingItem.getIntAttrName()); - if (derAttr != null) { - attrValue = anyUtils.newPlainAttrValue(); - attrValue.setStringValue(derAttr.getValue(any.getPlainAttrs())); + DerAttr<?> attr = any.getDerAttr(mappingItem.getIntAttrName()); + if (attr != null) { + PlainAttrValue attrValue = anyUtils.newPlainAttrValue(); + attrValue.setStringValue(attr.getValue(any.getPlainAttrs())); values.add(attrValue); } @@ -506,7 +631,7 @@ public final class MappingUtils { + "\n* IntAttrName {}" + "\n* IntMappingType {}" + "\n* Attribute values {}", - derAttr, mappingItem.getIntAttrName(), mappingItem.getIntMappingType(), values); + attr, mappingItem.getIntAttrName(), mappingItem.getIntMappingType(), values); } break; @@ -515,7 +640,7 @@ public final class MappingUtils { case AnyObjectKey: for (Any<?, ?, ?> any : anys) { AnyUtils anyUtils = anyUtilsFactory.getInstance(any); - attrValue = anyUtils.newPlainAttrValue(); + PlainAttrValue attrValue = anyUtils.newPlainAttrValue(); attrValue.setStringValue(any.getKey().toString()); values.add(attrValue); } @@ -524,7 +649,7 @@ public final class MappingUtils { case Username: for (Any<?, ?, ?> any : anys) { if (any instanceof User) { - attrValue = entityFactory.newEntity(UPlainAttrValue.class); + UPlainAttrValue attrValue = entityFactory.newEntity(UPlainAttrValue.class); attrValue.setStringValue(((User) any).getUsername()); values.add(attrValue); } @@ -534,7 +659,7 @@ public final class MappingUtils { case GroupName: for (Any<?, ?, ?> any : anys) { if (any instanceof Group) { - attrValue = entityFactory.newEntity(GPlainAttrValue.class); + GPlainAttrValue attrValue = entityFactory.newEntity(GPlainAttrValue.class); attrValue.setStringValue(((Group) any).getName()); values.add(attrValue); } @@ -542,7 +667,6 @@ public final class MappingUtils { break; case GroupOwnerSchema: - AnyTypeDAO anyTypeDAO = ApplicationContextProvider.getBeanFactory().getBean(AnyTypeDAO.class); Mapping uMapping = provision.getAnyType().equals(anyTypeDAO.findUser()) ? null : provision.getMapping(); @@ -562,7 +686,7 @@ public final class MappingUtils { } if (StringUtils.isNotBlank(groupOwnerValue)) { - attrValue = entityFactory.newEntity(GPlainAttrValue.class); + GPlainAttrValue attrValue = entityFactory.newEntity(GPlainAttrValue.class); attrValue.setStringValue(groupOwnerValue); values.add(attrValue); } @@ -573,9 +697,19 @@ public final class MappingUtils { default: } - LOG.debug("Retrieved values '{}'", values); + LOG.debug("Values for propagation: {}", values); - return values; + List<PlainAttrValue> transformed = values; + if (transform) { + for (MappingItemTransformer transformer : getMappingItemTransformers(mappingItem)) { + transformed = transformer.beforePropagation(transformed); + } + LOG.debug("Transformed values for propagation: {}", values); + } else { + LOG.debug("No transformation occurred"); + } + + return transformed; } /** @@ -585,7 +719,8 @@ public final class MappingUtils { * @param provision provision information * @return connObjectKey internal value */ - public static String getConnObjectKeyValue(final Any<?, ?, ?> any, final Provision provision) { + @Transactional(readOnly = true) + public String getConnObjectKeyValue(final Any<?, ?, ?> any, final Provision provision) { List<PlainAttrValue> values = getIntValues(provision, provision.getMapping().getConnObjectKeyItem(), Collections.<Any<?, ?, ?>>singletonList(any), null); return values == null || values.isEmpty() @@ -593,71 +728,139 @@ public final class MappingUtils { : values.get(0).getValueAsString(); } - public static MappingItem getConnObjectKeyItem(final Provision provision) { - Mapping mapping = null; - if (provision != null) { - mapping = provision.getMapping(); - } - - return mapping == null - ? null - : mapping.getConnObjectKeyItem(); - } - - public static List<MappingItem> getMappingItems(final Provision provision, final MappingPurpose purpose) { - List<? extends MappingItem> items = Collections.<MappingItem>emptyList(); - if (provision != null) { - items = provision.getMapping().getItems(); + /** + * Set attribute values, according to the given {@link MappingItem}, to any object from attribute received from + * connector. + * + * @param <T> any object + * @param mappingItem mapping item + * @param attr attribute received from connector + * @param anyTO any object + * @param anyUtils any utils + */ + @Transactional(readOnly = true) + public <T extends AnyTO> void setIntValues( + final MappingItem mappingItem, final Attribute attr, final T anyTO, final AnyUtils anyUtils) { + + List<Object> values = null; + if (attr != null) { + values = attr.getValue(); + for (MappingItemTransformer transformer : getMappingItemTransformers(mappingItem)) { + values = transformer.beforeSync(values); + } } + values = ListUtils.emptyIfNull(values); - List<MappingItem> result = new ArrayList<>(); + switch (mappingItem.getIntMappingType()) { + case UserKey: + case GroupKey: + case AnyObjectKey: + break; - switch (purpose) { - case SYNCHRONIZATION: - for (MappingItem item : items) { - if (MappingPurpose.PROPAGATION != item.getPurpose() - && MappingPurpose.NONE != item.getPurpose()) { + case Password: + if (anyTO instanceof UserTO && !values.isEmpty()) { + ((UserTO) anyTO).setPassword(ConnObjectUtils.getPassword(values.get(0))); + } + break; - result.add(item); - } + case Username: + if (anyTO instanceof UserTO) { + ((UserTO) anyTO).setUsername(values.isEmpty() || values.get(0) == null + ? null + : values.get(0).toString()); } break; - case PROPAGATION: - for (MappingItem item : items) { - if (MappingPurpose.SYNCHRONIZATION != item.getPurpose() - && MappingPurpose.NONE != item.getPurpose()) { + case GroupName: + if (anyTO instanceof GroupTO) { + ((GroupTO) anyTO).setName(values.isEmpty() || values.get(0) == null + ? null + : values.get(0).toString()); + } + break; - result.add(item); + case GroupOwnerSchema: + if (anyTO instanceof GroupTO && attr != null) { + // using a special attribute (with schema "", that will be ignored) for carrying the + // GroupOwnerSchema value + AttrTO attrTO = new AttrTO(); + attrTO.setSchema(StringUtils.EMPTY); + if (values.isEmpty() || values.get(0) == null) { + attrTO.getValues().add(StringUtils.EMPTY); + } else { + attrTO.getValues().add(values.get(0).toString()); } + + ((GroupTO) anyTO).getPlainAttrs().add(attrTO); } break; - case BOTH: - for (MappingItem item : items) { - if (MappingPurpose.NONE != item.getPurpose()) { - result.add(item); + case UserPlainSchema: + case GroupPlainSchema: + case AnyObjectPlainSchema: + AttrTO attrTO = new AttrTO(); + attrTO.setSchema(mappingItem.getIntAttrName()); + + PlainSchema schema = plainSchemaDAO.find(mappingItem.getIntAttrName()); + + for (Object value : values) { + AttrSchemaType schemaType = schema == null ? AttrSchemaType.String : schema.getType(); + if (value != null) { + PlainAttrValue attrValue = anyUtils.newPlainAttrValue(); + switch (schemaType) { + case String: + attrValue.setStringValue(value.toString()); + break; + + case Binary: + attrValue.setBinaryValue((byte[]) value); + break; + + default: + try { + attrValue.parseValue(schema, value.toString()); + } catch (ParsingValidationException e) { + LOG.error("While parsing provided value {}", value, e); + attrValue.setStringValue(value.toString()); + schemaType = AttrSchemaType.String; + } + break; + } + attrTO.getValues().add(attrValue.getValueAsString(schemaType)); } } + + anyTO.getPlainAttrs().add(attrTO); break; - case NONE: - for (MappingItem item : items) { - if (MappingPurpose.NONE == item.getPurpose()) { - result.add(item); + case UserDerivedSchema: + case GroupDerivedSchema: + case AnyObjectDerivedSchema: + attrTO = new AttrTO(); + attrTO.setSchema(mappingItem.getIntAttrName()); + anyTO.getDerAttrs().add(attrTO); + break; + + case UserVirtualSchema: + case GroupVirtualSchema: + case AnyObjectVirtualSchema: + attrTO = new AttrTO(); + attrTO.setSchema(mappingItem.getIntAttrName()); + + // virtual attributes don't get transformed, iterate over original attr.getValue() + for (Object value : (attr == null || attr.getValue() == null) + ? Collections.emptyList() : attr.getValue()) { + + if (value != null) { + attrTO.getValues().add(value.toString()); } } + + anyTO.getVirAttrs().add(attrTO); break; default: } - - return result; } - /** - * Private default constructor, for static-only classes. - */ - private MappingUtils() { - } } http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/misc/src/main/java/org/apache/syncope/core/misc/security/AuthDataAccessor.java ---------------------------------------------------------------------- diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/security/AuthDataAccessor.java b/core/misc/src/main/java/org/apache/syncope/core/misc/security/AuthDataAccessor.java index ff9e646..10eb235 100644 --- a/core/misc/src/main/java/org/apache/syncope/core/misc/security/AuthDataAccessor.java +++ b/core/misc/src/main/java/org/apache/syncope/core/misc/security/AuthDataAccessor.java @@ -72,6 +72,8 @@ public class AuthDataAccessor { protected static final Logger LOG = LoggerFactory.getLogger(AuthDataAccessor.class); + protected static final Encryptor ENCRYPTOR = Encryptor.getInstance(); + @Resource(name = "adminUser") protected String adminUser; @@ -102,7 +104,8 @@ public class AuthDataAccessor { @Autowired protected AuditManager auditManager; - protected final Encryptor encryptor = Encryptor.getInstance(); + @Autowired + protected MappingUtils mappingUtils; @Transactional(readOnly = true) public Domain findDomain(final String key) { @@ -158,7 +161,7 @@ public class AuthDataAccessor { } protected boolean authenticate(final User user, final String password) { - boolean authenticated = encryptor.verify(password, user.getCipherAlgorithm(), user.getPassword()); + boolean authenticated = ENCRYPTOR.verify(password, user.getCipherAlgorithm(), user.getPassword()); LOG.debug("{} authenticated on internal storage: {}", user.getUsername(), authenticated); for (Iterator<? extends ExternalResource> itor = getPassthroughResources(user).iterator(); @@ -167,7 +170,7 @@ public class AuthDataAccessor { ExternalResource resource = itor.next(); String connObjectKey = null; try { - connObjectKey = MappingUtils.getConnObjectKeyValue(user, resource.getProvision(anyTypeDAO.findUser())); + connObjectKey = mappingUtils.getConnObjectKeyValue(user, resource.getProvision(anyTypeDAO.findUser())); Uid uid = connFactory.getConnector(resource).authenticate(connObjectKey, password, null); if (uid != null) { authenticated = true; http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/GuardedStringSerializer.java ---------------------------------------------------------------------- diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/GuardedStringSerializer.java b/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/GuardedStringSerializer.java index c86fb2e..5c780f0 100644 --- a/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/GuardedStringSerializer.java +++ b/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/GuardedStringSerializer.java @@ -60,14 +60,14 @@ class GuardedStringSerializer extends JsonSerializer<GuardedString> { jgen.writeBooleanField("disposed", disposed); final StringBuilder cleartext = new StringBuilder(); - ((GuardedString) source).access(new GuardedString.Accessor() { + source.access(new GuardedString.Accessor() { @Override public void access(final char[] clearChars) { cleartext.append(clearChars); } }); - final byte[] encryptedBytes = + byte[] encryptedBytes = EncryptorFactory.getInstance().getDefaultEncryptor().encrypt(cleartext.toString().getBytes()); jgen.writeStringField("encryptedBytes", Base64.encode(encryptedBytes)); http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java ---------------------------------------------------------------------- diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java index 530b406..fd349a9 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java @@ -33,6 +33,7 @@ public interface ImplementationLookup extends SyncopeLoader { REPORTLET, ACCOUNT_RULE, PASSWORD_RULE, + MAPPING_ITEM_TRANSFORMER, TASKJOBDELEGATE, LOGIC_ACTIONS, PROPAGATION_ACTIONS, http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/MappingItem.java ---------------------------------------------------------------------- diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/MappingItem.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/MappingItem.java index 1a5380f..9f515ed 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/MappingItem.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/MappingItem.java @@ -18,6 +18,7 @@ */ package org.apache.syncope.core.persistence.api.entity.resource; +import java.util.List; import org.apache.syncope.common.lib.types.IntMappingType; import org.apache.syncope.common.lib.types.MappingPurpose; import org.apache.syncope.core.persistence.api.entity.Entity; @@ -56,4 +57,6 @@ public interface MappingItem extends Entity<Long> { void setPassword(boolean password); + List<String> getMappingItemTransformerClassNames(); + } http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAMappingItem.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAMappingItem.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAMappingItem.java index ae9a117..8dba286 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAMappingItem.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAMappingItem.java @@ -18,13 +18,19 @@ */ package org.apache.syncope.core.persistence.jpa.entity.resource; +import java.util.ArrayList; +import java.util.List; import javax.persistence.Basic; import javax.persistence.Cacheable; +import javax.persistence.CollectionTable; import javax.persistence.Column; +import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; +import javax.persistence.FetchType; import javax.persistence.Id; +import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.validation.constraints.Max; @@ -91,6 +97,16 @@ public class JPAMappingItem extends AbstractEntity<Long> implements MappingItem @Enumerated(EnumType.STRING) private MappingPurpose purpose; + /** + * (Optional) classes for MappingItem transformation. + */ + @ElementCollection(fetch = FetchType.EAGER) + @Column(name = "transformerClassName") + @CollectionTable(name = "MappingItem_Transformer", + joinColumns = + @JoinColumn(name = "mappingItem_id", referencedColumnName = "id")) + private List<String> mappingItemTransformerClassNames = new ArrayList<>(); + public JPAMappingItem() { super(); @@ -214,4 +230,10 @@ public class JPAMappingItem extends AbstractEntity<Long> implements MappingItem public void setPurpose(final MappingPurpose purpose) { this.purpose = purpose; } + + @Override + public List<String> getMappingItemTransformerClassNames() { + return mappingItemTransformerClassNames; + } + } http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java index 9ad2082..7879661 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java @@ -30,6 +30,7 @@ import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource; import org.apache.syncope.core.persistence.api.entity.resource.Mapping; import org.apache.syncope.core.persistence.api.entity.resource.MappingItem; import org.apache.syncope.core.persistence.api.entity.resource.Provision; +import org.apache.syncope.core.provisioning.api.data.MappingItemTransformer; import org.apache.syncope.core.provisioning.api.propagation.PropagationActions; public class ExternalResourceValidator extends AbstractValidator<ExternalResourceCheck, ExternalResource> { @@ -106,6 +107,27 @@ public class ExternalResourceValidator extends AbstractValidator<ExternalResourc isValid = false; } + for (MappingItem item : mapping.getItems()) { + for (String className : item.getMappingItemTransformerClassNames()) { + Class<?> actionsClass = null; + boolean isAssignable = false; + try { + actionsClass = Class.forName(className); + isAssignable = MappingItemTransformer.class.isAssignableFrom(actionsClass); + } catch (Exception e) { + LOG.error("Invalid MappingItemTransformer specified: {}", className, e); + } + + if (actionsClass == null || !isAssignable) { + context.buildConstraintViolationWithTemplate( + getTemplate(EntityViolationType.InvalidMapping, + "Invalid mapping item trasformer class name")). + addPropertyNode("mappingItemTransformerClassName").addConstraintViolation(); + isValid = false; + } + } + } + return isValid; } http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/MappingItemTransformer.java ---------------------------------------------------------------------- diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/MappingItemTransformer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/MappingItemTransformer.java new file mode 100644 index 0000000..ae11199 --- /dev/null +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/MappingItemTransformer.java @@ -0,0 +1,47 @@ +/* + * 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.api.data; + +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; + +/** + * Transforms values to be propagated to (or synchronizing from) external resources right before they leave (or enter) + * the Syncope internal storage. + * + * These transformations are not applied to virtual attribute values. + */ +public interface MappingItemTransformer { + + /** + * Invoked while preparing attribute values to be sent out to external resource during propagation. + * + * @param values original values + * @return transformed values + */ + List<PlainAttrValue> beforePropagation(List<PlainAttrValue> values); + + /** + * Invoked while reading attribute values from external resource during synchronization. + * + * @param values original values + * @return transformed values + */ + List<Object> beforeSync(List<Object> values); +} http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AsyncConnectorFacade.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AsyncConnectorFacade.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AsyncConnectorFacade.java index ae2d297..0b4340e 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AsyncConnectorFacade.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AsyncConnectorFacade.java @@ -120,7 +120,7 @@ public class AsyncConnectorFacade { Attribute attribute = null; - final ConnectorObject object = connector.getObject(objectClass, uid, options); + ConnectorObject object = connector.getObject(objectClass, uid, options); if (object == null) { LOG.debug("Object for '{}' not found", uid.getUidValue()); } else { @@ -137,9 +137,9 @@ public class AsyncConnectorFacade { final Uid uid, final OperationOptions options) { - final Set<Attribute> attributes = new HashSet<>(); + Set<Attribute> attributes = new HashSet<>(); - final ConnectorObject object = connector.getObject(objectClass, uid, options); + ConnectorObject object = connector.getObject(objectClass, uid, options); if (object == null) { LOG.debug("Object for '{}' not found", uid.getUidValue()); @@ -154,10 +154,10 @@ public class AsyncConnectorFacade { @Async public Future<Set<String>> getSchemaNames(final ConnectorFacade connector, final boolean includeSpecial) { - final Set<String> schemaNames = new HashSet<>(); + Set<String> schemaNames = new HashSet<>(); try { - final Schema schema = connector.schema(); + Schema schema = connector.schema(); for (ObjectClassInfo info : schema.getObjectClassInfo()) { for (AttributeInfo attrInfo : info.getAttributeInfo()) { if (includeSpecial || !AttributeUtil.isSpecialName(attrInfo.getName())) { @@ -175,10 +175,10 @@ public class AsyncConnectorFacade { @Async public Future<Set<ObjectClass>> getSupportedObjectClasses(final ConnectorFacade connector) { - final Set<ObjectClass> objectClasses = new HashSet<>(); + Set<ObjectClass> objectClasses = new HashSet<>(); try { - final Schema schema = connector.schema(); + Schema schema = connector.schema(); for (ObjectClassInfo info : schema.getObjectClassInfo()) { objectClasses.add(new ObjectClass(info.getType())); } http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandlerImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandlerImpl.java index 3907700..3d60089 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandlerImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandlerImpl.java @@ -34,7 +34,6 @@ import org.apache.syncope.common.lib.patch.AttrPatch; import org.apache.syncope.common.lib.to.AttrTO; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.IntMappingType; -import org.apache.syncope.common.lib.types.MappingPurpose; import org.apache.syncope.common.lib.types.PropagationByResource; import org.apache.syncope.common.lib.types.ResourceOperation; import org.apache.syncope.core.misc.MappingUtils; @@ -61,7 +60,6 @@ import org.apache.syncope.core.provisioning.api.cache.VirAttrCache; import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheValue; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.ConnectorObject; -import org.identityconnectors.framework.common.objects.OperationOptions; import org.identityconnectors.framework.common.objects.Uid; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -102,6 +100,9 @@ public class VirAttrHandlerImpl implements VirAttrHandler { @Autowired private VirAttrCache virAttrCache; + @Autowired + private MappingUtils mappingUtils; + @Override public VirSchema getVirSchema(final String virSchemaName) { VirSchema virtualSchema = null; @@ -122,8 +123,8 @@ public class VirAttrHandlerImpl implements VirAttrHandler { final PropagationByResource propByRes) { for (ExternalResource resource : resources) { - for (MappingItem mapItem : MappingUtils.getMappingItems( - resource.getProvision(any.getType()), MappingPurpose.PROPAGATION)) { + for (MappingItem mapItem + : MappingUtils.getPropagationMappingItems(resource.getProvision(any.getType()))) { if (schemaKey.equals(mapItem.getIntAttrName()) && mapItem.getIntMappingType() == mappingType) { propByRes.add(ResourceOperation.UPDATE, resource.getKey()); @@ -232,8 +233,8 @@ public class VirAttrHandlerImpl implements VirAttrHandler { } for (ExternalResource resource : externalResources) { - for (MappingItem mapItem : MappingUtils.getMappingItems( - resource.getProvision(any.getType()), MappingPurpose.PROPAGATION)) { + for (MappingItem mapItem + : MappingUtils.getPropagationMappingItems(resource.getProvision(any.getType()))) { if (virSchema.getKey().equals(mapItem.getIntAttrName()) && mapItem.getIntMappingType() == anyUtils.virIntMappingType()) { @@ -314,7 +315,7 @@ public class VirAttrHandlerImpl implements VirAttrHandler { LOG.debug("Search values into {},{}", resource, provision); try { - List<MappingItem> mappings = MappingUtils.getMappingItems(provision, MappingPurpose.BOTH); + List<MappingItem> mapItems = MappingUtils.getBothMappingItems(provision); ConnectorObject connectorObject; if (externalResources.containsKey(resource.getKey())) { @@ -323,33 +324,26 @@ public class VirAttrHandlerImpl implements VirAttrHandler { LOG.debug("Perform connection to {}", resource.getKey()); String connObjectKey = MappingUtils.getConnObjectKeyItem(provision) == null ? null - : MappingUtils.getConnObjectKeyValue(any, provision); + : mappingUtils.getConnObjectKeyValue(any, provision); if (StringUtils.isBlank(connObjectKey)) { throw new IllegalArgumentException("No ConnObjectKey found for " + resource.getKey()); } Connector connector = connFactory.getConnector(resource); - - OperationOptions oo = - connector.getOperationOptions(MappingUtils.getMatchingMappingItems(mappings, type)); - - connectorObject = - connector.getObject(provision.getObjectClass(), new Uid(connObjectKey), oo); + connectorObject = connector.getObject( + provision.getObjectClass(), + new Uid(connObjectKey), + connector.getOperationOptions(MappingUtils.getMatchingMappingItems(mapItems, type))); externalResources.put(resource.getKey(), connectorObject); } if (connectorObject != null) { - // ask for searched virtual attribute value - Collection<MappingItem> virAttrMappings = - MappingUtils.getMatchingMappingItems(mappings, schemaName, type); - - // the same virtual attribute could be mapped with one or more external attribute - for (MappingItem mapping : virAttrMappings) { - Attribute attribute = connectorObject.getAttributeByName(mapping.getExtAttrName()); - - if (attribute != null && attribute.getValue() != null) { - for (Object obj : attribute.getValue()) { + // the same virtual attribute could be mapped with one or more external attributes + for (MappingItem mapItem : MappingUtils.getMatchingMappingItems(mapItems, schemaName, type)) { + Attribute attr = connectorObject.getAttributeByName(mapItem.getExtAttrName()); + if (attr != null && attr.getValue() != null) { + for (Object obj : attr.getValue()) { if (obj != null) { virAttr.getValues().add(obj.toString()); } @@ -390,7 +384,7 @@ public class VirAttrHandlerImpl implements VirAttrHandler { public boolean evaluate(final ExternalResource resource) { return resource.getProvision(anyType) != null && !MappingUtils.getMatchingMappingItems( - MappingUtils.getMappingItems(resource.getProvision(anyType), MappingPurpose.BOTH), + MappingUtils.getBothMappingItems(resource.getProvision(anyType)), attr.getSchema().getKey(), type).isEmpty(); } });
