You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by fm...@apache.org on 2015/09/07 16:38:01 UTC
[2/2] syncope git commit: [SYNCOPE-691] merge from 1_2_X
[SYNCOPE-691] merge from 1_2_X
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/76e38c59
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/76e38c59
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/76e38c59
Branch: refs/heads/master
Commit: 76e38c59664f39245e6877c5853f5eae79835af4
Parents: f29fd4e 87e7543
Author: fmartelli <fa...@gmail.com>
Authored: Mon Sep 7 16:37:32 2015 +0200
Committer: fmartelli <fa...@gmail.com>
Committed: Mon Sep 7 16:37:32 2015 +0200
----------------------------------------------------------------------
.../apache/syncope/core/misc/MappingUtils.java | 9 +-
.../fit/core/reference/VirAttrITCase.java | 98 ++++++++++++++++++++
2 files changed, 102 insertions(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/76e38c59/core/misc/src/main/java/org/apache/syncope/core/misc/MappingUtils.java
----------------------------------------------------------------------
diff --cc core/misc/src/main/java/org/apache/syncope/core/misc/MappingUtils.java
index 5cb419d,0000000..3e7a802
mode 100644,000000..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
@@@ -1,672 -1,0 +1,671 @@@
+/*
+ * 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.misc;
+
+import org.apache.syncope.core.misc.policy.InvalidPasswordRuleConf;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.commons.jexl2.JexlContext;
+import org.apache.commons.jexl2.MapContext;
+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.mod.AttrMod;
+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.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.DerAttr;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.PlainAttr;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
- import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.api.entity.VirAttr;
- import org.apache.syncope.core.persistence.api.entity.VirSchema;
+import org.apache.syncope.core.persistence.api.entity.group.GPlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+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.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.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.identityconnectors.framework.common.FrameworkUtil;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.AttributeBuilder;
+import org.identityconnectors.framework.common.objects.AttributeUtil;
+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;
+
+public final class MappingUtils {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MappingUtils.class);
+
+ private static final Encryptor ENCRYPTOR = Encryptor.getInstance();
+
+ public static <T extends MappingItem> Collection<T> getMatchingMappingItems(
+ final Collection<T> items, final IntMappingType type) {
+
+ return CollectionUtils.select(items, new Predicate<T>() {
+
+ @Override
+ public boolean evaluate(final T item) {
+ return item.getIntMappingType() == type;
+ }
+ });
+ }
+
+ public static <T extends MappingItem> Collection<T> getMatchingMappingItems(
+ final Collection<T> items, final String intAttrName, final IntMappingType type) {
+
+ return CollectionUtils.select(items, new Predicate<T>() {
+
+ @Override
+ public boolean evaluate(final T item) {
+ return item.getIntMappingType() == type && intAttrName.equals(item.getIntAttrName());
+ }
+ });
+ }
+
+ public static <T extends MappingItem> Collection<T> getMatchingMappingItems(
+ final Collection<T> items, final String intAttrName) {
+
+ return CollectionUtils.select(items, new Predicate<T>() {
+
+ @Override
+ public boolean evaluate(final T item) {
+ return intAttrName.equals(item.getIntAttrName());
+ }
+ });
+ }
+
+ /**
+ * Prepare attributes for sending to a connector instance.
+ *
+ * @param any given any object
+ * @param password clear-text password
+ * @param changePwd whether password should be included for propagation attributes or not
+ * @param vAttrsToBeRemoved virtual attributes to be removed
+ * @param vAttrsToBeUpdated virtual attributes to be added
+ * @param enable whether any object must be enabled or not
+ * @param provision provision information
+ * @return connObjectLink + prepared attributes
+ */
+ public static Pair<String, Set<Attribute>> prepareAttrs(
+ final Any<?, ?, ?> any,
+ final String password,
+ final boolean changePwd,
+ final Set<String> vAttrsToBeRemoved,
+ final Map<String, AttrMod> vAttrsToBeUpdated,
+ final Boolean enable,
+ final Provision provision) {
+
+ 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;
+
+ for (MappingItem mapping : getMappingItems(provision, MappingPurpose.PROPAGATION)) {
+ LOG.debug("Processing schema {}", mapping.getIntAttrName());
+
+ try {
+ if (mapping.getIntMappingType() == IntMappingType.UserVirtualSchema
+ || mapping.getIntMappingType() == IntMappingType.GroupVirtualSchema
+ || mapping.getIntMappingType() == IntMappingType.AnyObjectVirtualSchema) {
+
+ LOG.debug("Expire entry cache {}-{}", any.getKey(), mapping.getIntAttrName());
+ virAttrCache.expire(any.getType().getKey(), any.getKey(), mapping.getIntAttrName());
+ }
+
+ Pair<String, Attribute> preparedAttr = prepareAttr(
+ provision, mapping, any, password, passwordGenerator, vAttrsToBeRemoved, vAttrsToBeUpdated);
+
+ if (preparedAttr != null && preparedAttr.getKey() != null) {
+ connObjectKey = preparedAttr.getKey();
+ }
+
+ if (preparedAttr != null && preparedAttr.getValue() != null) {
+ Attribute alreadyAdded = AttributeUtil.find(preparedAttr.getValue().getName(), attributes);
+
+ if (alreadyAdded == null) {
+ attributes.add(preparedAttr.getValue());
+ } else {
+ attributes.remove(alreadyAdded);
+
+ Set<Object> values = new HashSet<>(alreadyAdded.getValue());
+ values.addAll(preparedAttr.getValue().getValue());
+
+ attributes.add(AttributeBuilder.build(preparedAttr.getValue().getName(), values));
+ }
+ }
+ } catch (Exception e) {
+ LOG.debug("Attribute '{}' processing failed", mapping.getIntAttrName(), e);
+ }
+ }
+
+ Attribute connObjectKeyExtAttr =
+ AttributeUtil.find(getConnObjectKeyItem(provision).getExtAttrName(), attributes);
+ if (connObjectKeyExtAttr != null) {
+ attributes.remove(connObjectKeyExtAttr);
+ attributes.add(AttributeBuilder.build(getConnObjectKeyItem(provision).getExtAttrName(), connObjectKey));
+ }
+ attributes.add(evaluateNAME(any, provision, connObjectKey));
+
+ if (enable != null) {
+ attributes.add(AttributeBuilder.buildEnabled(enable));
+ }
+ if (!changePwd) {
+ Attribute pwdAttr = AttributeUtil.find(OperationalAttributes.PASSWORD_NAME, attributes);
+ if (pwdAttr != null) {
+ attributes.remove(pwdAttr);
+ }
+ }
+
+ return new ImmutablePair<>(connObjectKey, attributes);
+ }
+
+ /**
+ * Prepare an attribute to be sent to a connector instance.
+ *
+ * @param provision external resource
+ * @param mapItem mapping item for the given attribute
+ * @param any any object
+ * @param password clear-text password
+ * @param passwordGenerator password generator
+ * @param vAttrsToBeRemoved virtual attributes to be removed
+ * @param vAttrsToBeUpdated virtual attributes to be added
+ * @return connObjectLink + prepared attribute
+ */
+ @SuppressWarnings("unchecked")
+ private static Pair<String, Attribute> prepareAttr(
+ final Provision provision, final MappingItem mapItem,
+ final Any<?, ?, ?> any, final String password, final PasswordGenerator passwordGenerator,
+ final Set<String> vAttrsToBeRemoved, final Map<String, AttrMod> vAttrsToBeUpdated) {
+
+ 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) {
+ anys.add(any);
+ }
+ break;
+
+ 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);
+ }
+ } else if (any instanceof Group) {
+ anys.add(any);
+ }
+ break;
+
+ case ANY_OBJECT:
+ if (any instanceof AnyObject) {
+ anys.add(any);
+ }
+ break;
+
+ default:
+ }
+
+ List<PlainAttrValue> values = getIntValues(
+ provision, mapItem, anys, vAttrsToBeRemoved, vAttrsToBeUpdated);
+
- PlainSchema schema = null;
++ Schema schema = null;
+ boolean readOnlyVirSchema = false;
+ AttrSchemaType schemaType;
+ Pair<String, Attribute> result;
+
+ switch (mapItem.getIntMappingType()) {
+ 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;
+
+ case UserVirtualSchema:
+ case GroupVirtualSchema:
+ case AnyObjectVirtualSchema:
+ VirSchemaDAO virSchemaDAO = beanFactory.getBean(VirSchemaDAO.class);
- VirSchema virSchema = virSchemaDAO.find(mapItem.getIntAttrName());
- readOnlyVirSchema = (virSchema != null && virSchema.isReadonly());
++ schema = virSchemaDAO.find(mapItem.getIntAttrName());
++ readOnlyVirSchema = (schema != null && schema.isReadonly());
+ schemaType = AttrSchemaType.String;
+ break;
+
+ default:
+ schemaType = AttrSchemaType.String;
+ }
+
+ String extAttrName = mapItem.getExtAttrName();
+
+ LOG.debug("Define mapping for: "
+ + "\n* ExtAttrName " + extAttrName
+ + "\n* is connObjectKey " + mapItem.isConnObjectKey()
+ + "\n* is password " + (mapItem.isPassword() || mapItem.getIntMappingType() == IntMappingType.Password)
+ + "\n* mandatory condition " + mapItem.getMandatoryCondition()
+ + "\n* Schema " + mapItem.getIntAttrName()
+ + "\n* IntMappingType " + mapItem.getIntMappingType().toString()
+ + "\n* ClassType " + schemaType.getType().getName()
+ + "\n* Values " + values);
+
+ if (readOnlyVirSchema) {
+ result = null;
+ } else {
+ List<Object> objValues = new ArrayList<>();
+
+ for (PlainAttrValue value : values) {
+ if (FrameworkUtil.isSupportedAttributeType(schemaType.getType())) {
+ objValues.add(value.getValue());
+ } else {
+ objValues.add(value.getValueAsString());
+ }
+ }
+
+ if (mapItem.isConnObjectKey()) {
+ result = new ImmutablePair<>(objValues.iterator().next().toString(), null);
+ } else if (mapItem.isPassword() && any instanceof User) {
+ String passwordAttrValue = password;
+ if (StringUtils.isBlank(passwordAttrValue)) {
+ User user = (User) any;
+ if (user.canDecodePassword()) {
+ try {
+ passwordAttrValue = ENCRYPTOR.decode(user.getPassword(), user.getCipherAlgorithm());
+ } catch (Exception e) {
+ LOG.error("Could not decode password for {}", user, e);
+ }
+ } else if (provision.getResource().isRandomPwdIfNotProvided()) {
+ try {
+ passwordAttrValue = passwordGenerator.generate(user);
+ } catch (InvalidPasswordRuleConf e) {
+ LOG.error("Could not generate policy-compliant random password for {}", user, e);
+ }
+ }
+ }
+
+ if (passwordAttrValue == null) {
+ result = null;
+ } else {
+ result = new ImmutablePair<>(
+ null,
+ AttributeBuilder.buildPassword(passwordAttrValue.toCharArray()));
+ }
+ } else {
+ if ((schema != null && schema.isMultivalue())
+ || anyUtilsFactory.getInstance(any).getAnyTypeKind()
+ != mapItem.getIntMappingType().getAnyTypeKind()) {
+
+ result = new ImmutablePair<>(
+ null,
+ AttributeBuilder.build(extAttrName, objValues));
+ } else {
+ result = new ImmutablePair<>(
+ null, objValues.isEmpty()
+ ? AttributeBuilder.build(extAttrName)
+ : AttributeBuilder.build(extAttrName, objValues.iterator().next()));
+ }
+ }
+ }
+
+ 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>emptySet(), Collections.<String, AttrMod>emptyMap());
+ String connObjectKey = preparedAttr.getKey();
+
+ final Name groupOwnerName = evaluateNAME(any, provision, connObjectKey);
+ return groupOwnerName.getNameValue();
+ }
+
+ /**
+ * Get attribute values.
+ *
+ * @param provision provision information
+ * @param mappingItem mapping item
+ * @param anys any objects
+ * @param vAttrsToBeRemoved virtual attributes to be removed
+ * @param vAttrsToBeUpdated virtual attributes to be added
+ * @return attribute values.
+ */
+ public static List<PlainAttrValue> getIntValues(final Provision provision,
+ final MappingItem mappingItem, final List<Any<?, ?, ?>> anys,
+ final Set<String> vAttrsToBeRemoved, final Map<String, AttrMod> vAttrsToBeUpdated) {
+
+ 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);
+ List<PlainAttrValue> values = new ArrayList<>();
+ PlainAttrValue attrValue;
+ switch (mappingItem.getIntMappingType()) {
+ case UserPlainSchema:
+ case GroupPlainSchema:
+ case AnyObjectPlainSchema:
+ for (Any<?, ?, ?> any : anys) {
+ PlainAttr<?> attr = any.getPlainAttr(mappingItem.getIntAttrName());
+ if (attr != null) {
+ if (attr.getUniqueValue() != null) {
+ values.add(attr.getUniqueValue());
+ } else if (attr.getValues() != null) {
+ values.addAll(attr.getValues());
+ }
+ }
+
+ LOG.debug("Retrieved attribute {}"
+ + "\n* IntAttrName {}"
+ + "\n* IntMappingType {}"
+ + "\n* Attribute values {}",
+ attr, mappingItem.getIntAttrName(), mappingItem.getIntMappingType(), values);
+ }
+
+ break;
+
+ case UserVirtualSchema:
+ case GroupVirtualSchema:
+ case AnyObjectVirtualSchema:
+ for (Any<?, ?, ?> any : anys) {
+ AnyUtils anyUtils = anyUtilsFactory.getInstance(any);
+ VirAttr<?> virAttr = any.getVirAttr(mappingItem.getIntAttrName());
+ if (virAttr != null) {
+ if (vAttrsToBeRemoved != null && vAttrsToBeUpdated != null) {
+ if (vAttrsToBeUpdated.containsKey(mappingItem.getIntAttrName())) {
+ virAttr.getValues().clear();
+ virAttr.getValues().addAll(
+ vAttrsToBeUpdated.get(mappingItem.getIntAttrName()).getValuesToBeAdded());
+ } else if (vAttrsToBeRemoved.contains(mappingItem.getIntAttrName())) {
+ virAttr.getValues().clear();
+ } 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);
+ }
+ }
+ }
+
+ LOG.debug("Retrieved {} virtual attribute {}"
+ + "\n* IntAttrName {}"
+ + "\n* IntMappingType {}"
+ + "\n* Attribute values {}",
+ any.getClass().getSimpleName(),
+ virAttr, mappingItem.getIntAttrName(), mappingItem.getIntMappingType(), values);
+ }
+ break;
+
+ case UserDerivedSchema:
+ case GroupDerivedSchema:
+ 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()));
+ values.add(attrValue);
+ }
+
+ LOG.debug("Retrieved attribute {}"
+ + "\n* IntAttrName {}"
+ + "\n* IntMappingType {}"
+ + "\n* Attribute values {}",
+ derAttr, mappingItem.getIntAttrName(), mappingItem.getIntMappingType(), values);
+ }
+ break;
+
+ case UserKey:
+ case GroupKey:
+ case AnyObjectKey:
+ for (Any<?, ?, ?> any : anys) {
+ AnyUtils anyUtils = anyUtilsFactory.getInstance(any);
+ attrValue = anyUtils.newPlainAttrValue();
+ attrValue.setStringValue(any.getKey().toString());
+ values.add(attrValue);
+ }
+ break;
+
+ case Username:
+ for (Any<?, ?, ?> any : anys) {
+ if (any instanceof User) {
+ attrValue = entityFactory.newEntity(UPlainAttrValue.class);
+ attrValue.setStringValue(((User) any).getUsername());
+ values.add(attrValue);
+ }
+ }
+ break;
+
+ case GroupName:
+ for (Any<?, ?, ?> any : anys) {
+ if (any instanceof Group) {
+ attrValue = entityFactory.newEntity(GPlainAttrValue.class);
+ attrValue.setStringValue(((Group) any).getName());
+ values.add(attrValue);
+ }
+ }
+ break;
+
+ case GroupOwnerSchema:
+ AnyTypeDAO anyTypeDAO = ApplicationContextProvider.getBeanFactory().getBean(AnyTypeDAO.class);
+ Mapping uMapping = provision.getAnyType().equals(anyTypeDAO.findUser())
+ ? null
+ : provision.getMapping();
+ Mapping gMapping = provision.getAnyType().equals(anyTypeDAO.findGroup())
+ ? null
+ : provision.getMapping();
+
+ for (Any<?, ?, ?> any : anys) {
+ if (any instanceof Group) {
+ Group group = (Group) any;
+ String groupOwnerValue = null;
+ if (group.getUserOwner() != null && uMapping != null) {
+ groupOwnerValue = getGroupOwnerValue(provision, group.getUserOwner());
+ }
+ if (group.getGroupOwner() != null && gMapping != null) {
+ groupOwnerValue = getGroupOwnerValue(provision, group.getGroupOwner());
+ }
+
+ if (StringUtils.isNotBlank(groupOwnerValue)) {
+ attrValue = entityFactory.newEntity(GPlainAttrValue.class);
+ attrValue.setStringValue(groupOwnerValue);
+ values.add(attrValue);
+ }
+ }
+ }
+ break;
+
+ default:
+ }
+
+ LOG.debug("Retrieved values '{}'", values);
+
+ return values;
+ }
+
+ /**
+ * Get connObjectKey internal value.
+ *
+ * @param any any object
+ * @param provision provision information
+ * @return connObjectKey internal value
+ */
+ public static String getConnObjectKeyValue(final Any<?, ?, ?> any, final Provision provision) {
+ List<PlainAttrValue> values = getIntValues(provision, provision.getMapping().getConnObjectKeyItem(),
+ Collections.<Any<?, ?, ?>>singletonList(any), null, null);
+ return values == null || values.isEmpty()
+ ? null
+ : 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();
+ }
+
+ 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;
+ }
+
+ /**
+ * Private default constructor, for static-only classes.
+ */
+ private MappingUtils() {
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/76e38c59/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/VirAttrITCase.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/VirAttrITCase.java
index cb166a9,0000000..5ab153f
mode 100644,000000..100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/VirAttrITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/VirAttrITCase.java
@@@ -1,616 -1,0 +1,714 @@@
+/*
+ * 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.fit.core.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Locale;
+import java.util.Map;
++import org.apache.commons.collections4.CollectionUtils;
++import org.apache.commons.collections4.Predicate;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.syncope.common.lib.mod.AttrMod;
+import org.apache.syncope.common.lib.mod.StatusMod;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.lib.to.ConnObjectTO;
+import org.apache.syncope.common.lib.to.MappingItemTO;
+import org.apache.syncope.common.lib.to.MappingTO;
+import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.to.ProvisionTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.ConnConfProperty;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.common.rest.api.service.ResourceService;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class VirAttrITCase extends AbstractITCase {
+
+ @Test
+ public void issueSYNCOPE16() {
+ UserTO userTO = UserITCase.getUniqueSampleTO("issue16@apache.org");
+
+ MembershipTO membershipTO = new MembershipTO();
+ membershipTO.setRightKey(8L);
+ userTO.getMemberships().add(membershipTO);
+
+ // 1. create user
+ UserTO actual = createUser(userTO);
+ assertNotNull(actual);
+
+ // 2. check for virtual attribute value
+ actual = userService.read(actual.getKey());
+ assertNotNull(actual);
+ assertEquals("virtualvalue", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+ UserMod userMod = new UserMod();
+ userMod.setKey(actual.getKey());
+ userMod.getVirAttrsToRemove().add("virtualdata");
+ userMod.getVirAttrsToUpdate().add(attrMod("virtualdata", "virtualupdated"));
+
+ // 3. update virtual attribute
+ actual = updateUser(userMod);
+ assertNotNull(actual);
+
+ // 4. check for virtual attribute value
+ actual = userService.read(actual.getKey());
+ assertNotNull(actual);
+ assertEquals("virtualupdated", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
+ }
+
+ @Test
+ public void issueSYNCOPE260() {
+ // ----------------------------------
+ // create user and check virtual attribute value propagation
+ // ----------------------------------
+ UserTO userTO = UserITCase.getUniqueSampleTO("260@a.com");
+ userTO.getResources().add(RESOURCE_NAME_WS2);
+
+ userTO = createUser(userTO);
+ assertNotNull(userTO);
+ assertFalse(userTO.getPropagationStatusTOs().isEmpty());
+ assertEquals(RESOURCE_NAME_WS2, userTO.getPropagationStatusTOs().get(0).getResource());
+ assertEquals(PropagationTaskExecStatus.SUBMITTED, userTO.getPropagationStatusTOs().get(0).getStatus());
+
+ ConnObjectTO connObjectTO =
+ resourceService.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
+ assertNotNull(connObjectTO);
+ assertEquals("virtualvalue", connObjectTO.getPlainAttrMap().get("NAME").getValues().get(0));
+ // ----------------------------------
+
+ // ----------------------------------
+ // update user virtual attribute and check virtual attribute value update propagation
+ // ----------------------------------
+ UserMod userMod = new UserMod();
+ userMod.setKey(userTO.getKey());
+
+ AttrMod attrMod = new AttrMod();
+ attrMod.setSchema("virtualdata");
+ attrMod.getValuesToBeRemoved().add("virtualvalue");
+ attrMod.getValuesToBeAdded().add("virtualvalue2");
+
+ userMod.getVirAttrsToUpdate().add(attrMod);
+
+ userTO = updateUser(userMod);
+ assertNotNull(userTO);
+ assertFalse(userTO.getPropagationStatusTOs().isEmpty());
+ assertEquals("ws-target-resource-2", userTO.getPropagationStatusTOs().get(0).getResource());
+ assertEquals(PropagationTaskExecStatus.SUBMITTED, userTO.getPropagationStatusTOs().get(0).getStatus());
+
+ connObjectTO = resourceService.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
+ assertNotNull(connObjectTO);
+ assertEquals("virtualvalue2", connObjectTO.getPlainAttrMap().get("NAME").getValues().get(0));
+ // ----------------------------------
+
+ // ----------------------------------
+ // suspend/reactivate user and check virtual attribute value (unchanged)
+ // ----------------------------------
+ StatusMod statusMod = new StatusMod();
+ statusMod.setKey(userTO.getKey());
+ statusMod.setType(StatusMod.ModType.SUSPEND);
+ userTO = userService.status(statusMod).readEntity(UserTO.class);
+ assertEquals("suspended", userTO.getStatus());
+
+ connObjectTO = resourceService.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
+ assertNotNull(connObjectTO);
+ assertFalse(connObjectTO.getPlainAttrMap().get("NAME").getValues().isEmpty());
+ assertEquals("virtualvalue2", connObjectTO.getPlainAttrMap().get("NAME").getValues().get(0));
+
+ statusMod = new StatusMod();
+ statusMod.setKey(userTO.getKey());
+ statusMod.setType(StatusMod.ModType.REACTIVATE);
+ userTO = userService.status(statusMod).readEntity(UserTO.class);
+ assertEquals("active", userTO.getStatus());
+
+ connObjectTO = resourceService.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
+ assertNotNull(connObjectTO);
+ assertFalse(connObjectTO.getPlainAttrMap().get("NAME").getValues().isEmpty());
+ assertEquals("virtualvalue2", connObjectTO.getPlainAttrMap().get("NAME").getValues().get(0));
+ // ----------------------------------
+
+ // ----------------------------------
+ // update user attribute and check virtual attribute value (unchanged)
+ // ----------------------------------
+ userMod = new UserMod();
+ userMod.setKey(userTO.getKey());
+
+ attrMod = new AttrMod();
+ attrMod.setSchema("surname");
+ attrMod.getValuesToBeRemoved().add("Surname");
+ attrMod.getValuesToBeAdded().add("Surname2");
+
+ userMod.getPlainAttrsToUpdate().add(attrMod);
+
+ userTO = updateUser(userMod);
+ assertNotNull(userTO);
+ assertFalse(userTO.getPropagationStatusTOs().isEmpty());
+ assertEquals(RESOURCE_NAME_WS2, userTO.getPropagationStatusTOs().get(0).getResource());
+ assertEquals(PropagationTaskExecStatus.SUBMITTED, userTO.getPropagationStatusTOs().get(0).getStatus());
+
+ connObjectTO = resourceService.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
+ assertNotNull(connObjectTO);
+ assertEquals("Surname2", connObjectTO.getPlainAttrMap().get("SURNAME").getValues().get(0));
+
+ // attribute "name" mapped on virtual attribute "virtualdata" shouldn't be changed
+ assertFalse(connObjectTO.getPlainAttrMap().get("NAME").getValues().isEmpty());
+ assertEquals("virtualvalue2", connObjectTO.getPlainAttrMap().get("NAME").getValues().get(0));
+ // ----------------------------------
+
+ // ----------------------------------
+ // remove user virtual attribute and check virtual attribute value (reset)
+ // ----------------------------------
+ userMod = new UserMod();
+ userMod.setKey(userTO.getKey());
+ userMod.getVirAttrsToRemove().add("virtualdata");
+
+ userTO = updateUser(userMod);
+ assertNotNull(userTO);
+ assertTrue(userTO.getVirAttrs().isEmpty());
+ assertFalse(userTO.getPropagationStatusTOs().isEmpty());
+ assertEquals(RESOURCE_NAME_WS2, userTO.getPropagationStatusTOs().get(0).getResource());
+ assertEquals(PropagationTaskExecStatus.SUBMITTED, userTO.getPropagationStatusTOs().get(0).getStatus());
+
+ connObjectTO = resourceService.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
+ assertNotNull(connObjectTO);
+
+ // attribute "name" mapped on virtual attribute "virtualdata" should be reset
+ assertTrue(connObjectTO.getPlainAttrMap().get("NAME").getValues() == null
+ || connObjectTO.getPlainAttrMap().get("NAME").getValues().isEmpty());
+ // ----------------------------------
+ }
+
+ @Test
+ public void virAttrCache() {
+ UserTO userTO = UserITCase.getUniqueSampleTO("virattrcache@apache.org");
+ userTO.getVirAttrs().clear();
+
+ AttrTO virAttrTO = new AttrTO();
+ virAttrTO.setSchema("virtualdata");
+ virAttrTO.getValues().add("virattrcache");
+ userTO.getVirAttrs().add(virAttrTO);
+
+ userTO.getMemberships().clear();
+ userTO.getResources().clear();
+ userTO.getResources().add(RESOURCE_NAME_DBVIRATTR);
+
+ // 1. create user
+ UserTO actual = createUser(userTO);
+ assertNotNull(actual);
+
+ // 2. check for virtual attribute value
+ actual = userService.read(actual.getKey());
+ assertEquals("virattrcache", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+ // 3. update virtual attribute directly
+ JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+
+ String value = jdbcTemplate.queryForObject(
+ "SELECT USERNAME FROM testsync WHERE ID=?", String.class, actual.getKey());
+ assertEquals("virattrcache", value);
+
+ jdbcTemplate.update("UPDATE testsync set USERNAME='virattrcache2' WHERE ID=?", actual.getKey());
+
+ value = jdbcTemplate.queryForObject(
+ "SELECT USERNAME FROM testsync WHERE ID=?", String.class, actual.getKey());
+ assertEquals("virattrcache2", value);
+
+ // 4. check for cached attribute value
+ actual = userService.read(actual.getKey());
+ assertEquals("virattrcache", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+ UserMod userMod = new UserMod();
+ userMod.setKey(actual.getKey());
+
+ AttrMod virtualdata = new AttrMod();
+ virtualdata.setSchema("virtualdata");
+ virtualdata.getValuesToBeAdded().add("virtualupdated");
+
+ userMod.getVirAttrsToRemove().add("virtualdata");
+ userMod.getVirAttrsToUpdate().add(virtualdata);
+
+ // 5. update virtual attribute
+ actual = updateUser(userMod);
+ assertNotNull(actual);
+
+ // 6. check for virtual attribute value
+ actual = userService.read(actual.getKey());
+ assertNotNull(actual);
+ assertEquals("virtualupdated", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
+ }
+
+ @Test
+ public void issueSYNCOPE397() {
+ ResourceTO csv = resourceService.read(RESOURCE_NAME_CSV);
+ MappingTO origMapping = SerializationUtils.clone(csv.getProvisions().get(0).getMapping());
+ try {
+ // change mapping of resource-csv
+ assertNotNull(origMapping);
+ for (MappingItemTO item : csv.getProvisions().get(0).getMapping().getItems()) {
+ if ("email".equals(item.getIntAttrName())) {
+ // unset internal attribute mail and set virtual attribute virtualdata as mapped to external email
+ item.setIntMappingType(IntMappingType.UserVirtualSchema);
+ item.setIntAttrName("virtualdata");
+ item.setPurpose(MappingPurpose.BOTH);
+ item.setExtAttrName("email");
+ }
+ }
+
+ resourceService.update(csv);
+ csv = resourceService.read(RESOURCE_NAME_CSV);
+ assertNotNull(csv.getProvisions().get(0).getMapping());
+
+ boolean found = false;
+ for (MappingItemTO item : csv.getProvisions().get(0).getMapping().getItems()) {
+ if ("email".equals(item.getExtAttrName()) && "virtualdata".equals(item.getIntAttrName())) {
+ found = true;
+ }
+ }
+
+ assertTrue(found);
+
+ // create a new user
+ UserTO userTO = UserITCase.getUniqueSampleTO("syncope397@syncope.apache.org");
+ userTO.getAuxClasses().add("csv");
+ userTO.getResources().clear();
+ userTO.getMemberships().clear();
+ userTO.getDerAttrs().clear();
+ userTO.getVirAttrs().clear();
+
+ userTO.getDerAttrs().add(attrTO("csvuserid", null));
+ userTO.getDerAttrs().add(attrTO("cn", null));
+ userTO.getVirAttrs().add(attrTO("virtualdata", "test@testone.org"));
+ // assign resource-csv to user
+ userTO.getResources().add(RESOURCE_NAME_CSV);
+ // save user
+ UserTO created = createUser(userTO);
+ // make std controls about user
+ assertNotNull(created);
+ assertTrue(RESOURCE_NAME_CSV.equals(created.getResources().iterator().next()));
++ assertEquals("test@testone.org", created.getVirAttrs().iterator().next().getValues().get(0));
++
+ // update user
+ UserTO toBeUpdated = userService.read(created.getKey());
+ UserMod userMod = new UserMod();
+ userMod.setKey(toBeUpdated.getKey());
+ userMod.setPassword("password234");
+ // assign new resource to user
+ userMod.getResourcesToAdd().add(RESOURCE_NAME_WS2);
+ //modify virtual attribute
+ userMod.getVirAttrsToRemove().add("virtualdata");
+ userMod.getVirAttrsToUpdate().add(attrMod("virtualdata", "test@testoneone.com"));
+
+ // check Syncope change password
+ StatusMod pwdPropRequest = new StatusMod();
+ pwdPropRequest.setOnSyncope(true);
+ pwdPropRequest.getResources().add(RESOURCE_NAME_WS2);
+ userMod.setPwdPropRequest(pwdPropRequest);
+
+ toBeUpdated = updateUser(userMod);
+ assertNotNull(toBeUpdated);
+ assertEquals("test@testoneone.com", toBeUpdated.getVirAttrs().iterator().next().getValues().get(0));
+ // check if propagates correctly with assertEquals on size of tasks list
+ assertEquals(2, toBeUpdated.getPropagationStatusTOs().size());
+ } finally {
+ // restore mapping of resource-csv
+ csv.getProvisions().get(0).setMapping(origMapping);
+ resourceService.update(csv);
+ }
+ }
+
+ @Test
+ public void issueSYNCOPE442() {
+ UserTO userTO = UserITCase.getUniqueSampleTO("syncope442@apache.org");
+ userTO.getVirAttrs().clear();
+
+ AttrTO virAttrTO = new AttrTO();
+ virAttrTO.setSchema("virtualdata");
+ virAttrTO.getValues().add("virattrcache");
+ userTO.getVirAttrs().add(virAttrTO);
+
+ userTO.getMemberships().clear();
+ userTO.getResources().clear();
+ userTO.getResources().add(RESOURCE_NAME_DBVIRATTR);
+
+ // 1. create user
+ UserTO actual = createUser(userTO);
+ assertNotNull(actual);
+
+ // 2. check for virtual attribute value
+ actual = userService.read(actual.getKey());
+ assertEquals("virattrcache", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+ // ----------------------------------------
+ // 3. force cache expiring without any modification
+ // ----------------------------------------
+ String jdbcURL = null;
+ ConnInstanceTO connInstanceTO = connectorService.readByResource(
+ RESOURCE_NAME_DBVIRATTR, Locale.ENGLISH.getLanguage());
+ for (ConnConfProperty prop : connInstanceTO.getConfiguration()) {
+ if ("jdbcUrlTemplate".equals(prop.getSchema().getName())) {
+ jdbcURL = prop.getValues().iterator().next().toString();
+ prop.getValues().clear();
+ prop.getValues().add("jdbc:h2:tcp://localhost:9092/xxx");
+ }
+ }
+
+ connectorService.update(connInstanceTO);
+
+ UserMod userMod = new UserMod();
+ userMod.setKey(actual.getKey());
+
+ AttrMod virtualdata = new AttrMod();
+ virtualdata.setSchema("virtualdata");
+ virtualdata.getValuesToBeAdded().add("virtualupdated");
+
+ userMod.getVirAttrsToRemove().add("virtualdata");
+ userMod.getVirAttrsToUpdate().add(virtualdata);
+
+ actual = updateUser(userMod);
+ assertNotNull(actual);
+ // ----------------------------------------
+
+ // ----------------------------------------
+ // 4. update virtual attribute
+ // ----------------------------------------
+ final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+
+ String value = jdbcTemplate.queryForObject(
+ "SELECT USERNAME FROM testsync WHERE ID=?", String.class, actual.getKey());
+ assertEquals("virattrcache", value);
+
+ jdbcTemplate.update("UPDATE testsync set USERNAME='virattrcache2' WHERE ID=?", actual.getKey());
+
+ value = jdbcTemplate.queryForObject(
+ "SELECT USERNAME FROM testsync WHERE ID=?", String.class, actual.getKey());
+ assertEquals("virattrcache2", value);
+ // ----------------------------------------
+
+ actual = userService.read(actual.getKey());
+ assertEquals("virattrcache", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+ // ----------------------------------------
+ // 5. restore connector
+ // ----------------------------------------
+ for (ConnConfProperty prop : connInstanceTO.getConfiguration()) {
+ if ("jdbcUrlTemplate".equals(prop.getSchema().getName())) {
+ prop.getValues().clear();
+ prop.getValues().add(jdbcURL);
+ }
+ }
+
+ connectorService.update(connInstanceTO);
+ // ----------------------------------------
+
+ actual = userService.read(actual.getKey());
+ assertEquals("virattrcache2", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
+ }
+
+ @Test
+ public void issueSYNCOPE436() {
+ UserTO userTO = UserITCase.getUniqueSampleTO("syncope436@syncope.apache.org");
+ userTO.getMemberships().clear();
+ userTO.getResources().clear();
+ userTO.getResources().add(RESOURCE_NAME_LDAP);
+ userTO.getVirAttrs().add(attrTO("virtualReadOnly", "readOnly"));
+ userTO = createUser(userTO);
+ //Finding no values because the virtual attribute is readonly
+ assertTrue(userTO.getVirAttrMap().get("virtualReadOnly").getValues().isEmpty());
+ }
+
+ @Test
+ public void issueSYNCOPE453() {
+ final String resourceName = "issueSYNCOPE453-Res-" + getUUIDString();
+ final String groupName = "issueSYNCOPE453-Group-" + getUUIDString();
+
+ // -------------------------------------------
+ // Create a resource ad-hoc
+ // -------------------------------------------
+ ResourceTO resourceTO = new ResourceTO();
+
+ resourceTO.setKey(resourceName);
+ resourceTO.setConnector(107L);
+
+ ProvisionTO provisionTO = new ProvisionTO();
+ provisionTO.setAnyType(AnyTypeKind.USER.name());
+ provisionTO.setObjectClass(ObjectClass.ACCOUNT_NAME);
+ resourceTO.getProvisions().add(provisionTO);
+
+ MappingTO mapping = new MappingTO();
+ provisionTO.setMapping(mapping);
+
+ MappingItemTO item = new MappingItemTO();
+ item.setIntAttrName("aLong");
+ item.setIntMappingType(IntMappingType.UserPlainSchema);
+ item.setExtAttrName("ID");
+ item.setPurpose(MappingPurpose.PROPAGATION);
+ item.setConnObjectKey(true);
+ mapping.setConnObjectKeyItem(item);
+
+ item = new MappingItemTO();
+ item.setExtAttrName("USERNAME");
+ item.setIntAttrName("username");
+ item.setIntMappingType(IntMappingType.Username);
+ item.setPurpose(MappingPurpose.PROPAGATION);
+ mapping.getItems().add(item);
+
+ item = new MappingItemTO();
+ item.setExtAttrName("EMAIL");
+ item.setIntAttrName("rvirtualdata");
+ item.setIntMappingType(IntMappingType.GroupVirtualSchema);
+ item.setPurpose(MappingPurpose.PROPAGATION);
+ mapping.getItems().add(item);
+
+ assertNotNull(getObject(
+ resourceService.create(resourceTO).getLocation(), ResourceService.class, ResourceTO.class));
+ // -------------------------------------------
+
+ // -------------------------------------------
+ // Create a VirAttrITCase ad-hoc
+ // -------------------------------------------
+ GroupTO groupTO = new GroupTO();
+ groupTO.setName(groupName);
+ groupTO.setRealm("/");
+ groupTO.getVirAttrs().add(attrTO("rvirtualdata", "ml@group.it"));
+ groupTO.getResources().add(RESOURCE_NAME_LDAP);
+ groupTO = createGroup(groupTO);
+ assertEquals(1, groupTO.getVirAttrs().size());
+ assertEquals("ml@group.it", groupTO.getVirAttrs().iterator().next().getValues().get(0));
+ // -------------------------------------------
+
+ // -------------------------------------------
+ // Create new user
+ // -------------------------------------------
+ UserTO userTO = UserITCase.getUniqueSampleTO("syncope453@syncope.apache.org");
+ userTO.getPlainAttrs().add(attrTO("aLong", "123"));
+ userTO.getResources().clear();
+ userTO.getResources().add(resourceName);
+ userTO.getVirAttrs().clear();
+ userTO.getDerAttrs().clear();
+ userTO.getMemberships().clear();
+
+ MembershipTO membership = new MembershipTO();
+ membership.setRightKey(groupTO.getKey());
+ userTO.getMemberships().add(membership);
+
+ userTO = createUser(userTO);
+ assertEquals(2, userTO.getPropagationStatusTOs().size());
+ assertTrue(userTO.getPropagationStatusTOs().get(0).getStatus().isSuccessful());
+ assertTrue(userTO.getPropagationStatusTOs().get(1).getStatus().isSuccessful());
+
+ JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+
+ final Map<String, Object> actuals = jdbcTemplate.queryForMap(
+ "SELECT id, surname, email FROM testsync WHERE id=?",
+ new Object[] { Integer.parseInt(userTO.getPlainAttrMap().get("aLong").getValues().get(0)) });
+
+ assertEquals(userTO.getPlainAttrMap().get("aLong").getValues().get(0), actuals.get("id").toString());
+ assertEquals("ml@group.it", actuals.get("email"));
+ // -------------------------------------------
+
+ // -------------------------------------------
+ // Delete resource and group ad-hoc
+ // -------------------------------------------
+ resourceService.delete(resourceName);
+ groupService.delete(groupTO.getKey());
+ // -------------------------------------------
+ }
+
+ @Test
+ public void issueSYNCOPE459() {
+ UserTO userTO = UserITCase.getUniqueSampleTO("syncope459@apache.org");
+ userTO.getResources().clear();
+ userTO.getMemberships().clear();
+ userTO.getVirAttrs().clear();
+
+ AttrTO virtualReadOnly = attrTO("virtualReadOnly", "");
+ virtualReadOnly.getValues().clear();
+
+ userTO.getVirAttrs().add(virtualReadOnly);
+
+ userTO = createUser(userTO);
+
+ assertNotNull(userTO.getVirAttrMap().get("virtualReadOnly"));
+
+ UserMod userMod = new UserMod();
+ userMod.setKey(userTO.getKey());
+
+ AttrMod virtualdata = new AttrMod();
+ virtualdata.setSchema("virtualdata");
+
+ userMod.getVirAttrsToUpdate().add(virtualdata);
+
+ userTO = updateUser(userMod);
+ assertNotNull(userTO.getVirAttrMap().get("virtualdata"));
+ }
+
+ @Test
+ public void issueSYNCOPE501() {
+ // 1. create user and propagate him on resource-db-virattr
+ UserTO userTO = UserITCase.getUniqueSampleTO("syncope501@apache.org");
+ userTO.getResources().clear();
+ userTO.getMemberships().clear();
+ userTO.getVirAttrs().clear();
+
+ userTO.getResources().add(RESOURCE_NAME_DBVIRATTR);
+
+ // virtualdata is mapped with username
+ final AttrTO virtualData = attrTO("virtualdata", "syncope501@apache.org");
+ userTO.getVirAttrs().add(virtualData);
+
+ userTO = createUser(userTO);
+
+ assertNotNull(userTO.getVirAttrMap().get("virtualdata"));
+ assertEquals("syncope501@apache.org", userTO.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+ // 2. update virtual attribute
+ UserMod userMod = new UserMod();
+ userMod.setKey(userTO.getKey());
+
+ final StatusMod statusMod = new StatusMod();
+ statusMod.getResources().addAll(Collections.<String>emptySet());
+ statusMod.setOnSyncope(false);
+
+ userMod.setPwdPropRequest(statusMod);
+ // change virtual attribute value
+ final AttrMod virtualDataMod = new AttrMod();
+ virtualDataMod.setSchema("virtualdata");
+ virtualDataMod.getValuesToBeAdded().add("syncope501_updated@apache.org");
+ virtualDataMod.getValuesToBeRemoved().add("syncope501@apache.org");
+ userMod.getVirAttrsToUpdate().add(virtualDataMod);
+ userMod.getVirAttrsToRemove().add("virtualdata");
+
+ userTO = updateUser(userMod);
+ assertNotNull(userTO);
+
+ // 3. check that user virtual attribute has really been updated
+ assertFalse(userTO.getVirAttrMap().get("virtualdata").getValues().isEmpty());
+ assertEquals("syncope501_updated@apache.org", userTO.getVirAttrMap().get("virtualdata").getValues().get(0));
+ }
++
++ @Test
++ public void issueSYNCOPE691() {
++ final String res = RESOURCE_NAME_LDAP + "691";
++
++ ResourceTO ldap = resourceService.read(RESOURCE_NAME_LDAP);
++ ldap.setKey(res);
++
++ try {
++
++ CollectionUtils.filterInverse(ldap.getProvision(AnyTypeKind.USER.name()).getMapping().getItems(),
++ new Predicate<MappingItemTO>() {
++
++ @Override
++ public boolean evaluate(final MappingItemTO item) {
++ return "mail".equals(item.getExtAttrName());
++ }
++ });
++
++ final MappingItemTO mail = new MappingItemTO();
++ // unset internal attribute mail and set virtual attribute virtualdata as mapped to external email
++ mail.setIntMappingType(IntMappingType.UserVirtualSchema);
++ mail.setIntAttrName("virtualdata");
++ mail.setPurpose(MappingPurpose.BOTH);
++ mail.setExtAttrName("mail");
++
++ ldap.getProvision(AnyTypeKind.USER.name()).getMapping().getItems().add(mail);
++
++ resourceService.create(ldap);
++
++ ldap = resourceService.read(res);
++ assertNotNull(ldap.getProvision(AnyTypeKind.USER.name()).getMapping());
++
++ assertTrue(CollectionUtils.exists(ldap.getProvision(AnyTypeKind.USER.name()).getMapping().getItems(),
++ new Predicate<MappingItemTO>() {
++
++ @Override
++ public boolean evaluate(final MappingItemTO item) {
++ return "mail".equals(item.getExtAttrName()) && "virtualdata".equals(item.getIntAttrName());
++ }
++ }));
++
++ // create a new user
++ UserTO userTO = UserITCase.getUniqueSampleTO("syncope691@syncope.apache.org");
++ userTO.getResources().clear();
++ userTO.getMemberships().clear();
++ userTO.getDerAttrs().clear();
++ userTO.getVirAttrs().clear();
++
++ final AttrTO emailTO = new AttrTO();
++ emailTO.setSchema("virtualdata");
++ emailTO.getValues().add("test@issue691.dom1.org");
++ emailTO.getValues().add("test@issue691.dom2.org");
++
++ userTO.getVirAttrs().add(emailTO);
++ // assign resource-ldap691 to user
++ userTO.getResources().add(res);
++ // save user
++ UserTO created = createUser(userTO);
++ // make std controls about user
++ assertNotNull(created);
++ assertTrue(res.equals(created.getResources().iterator().next()));
++
++ assertEquals(2, created.getVirAttrs().iterator().next().getValues().size(), 0);
++ assertTrue(created.getVirAttrs().iterator().next().getValues().contains("test@issue691.dom1.org"));
++ assertTrue(created.getVirAttrs().iterator().next().getValues().contains("test@issue691.dom2.org"));
++
++ // update user
++ UserMod userMod = new UserMod();
++ userMod.setKey(created.getKey());
++ //modify virtual attribute
++ userMod.getVirAttrsToRemove().add("virtualdata");
++
++ final AttrMod emailMod = new AttrMod();
++ emailMod.setSchema("virtualdata");
++ emailMod.getValuesToBeAdded().add("test@issue691.dom3.org");
++ emailMod.getValuesToBeAdded().add("test@issue691.dom4.org");
++
++ userMod.getVirAttrsToUpdate().add(emailMod);
++
++ final UserTO updated = updateUser(userMod);
++ assertNotNull(updated);
++ assertEquals(2, updated.getVirAttrs().iterator().next().getValues().size(), 0);
++ assertTrue(updated.getVirAttrs().iterator().next().getValues().contains("test@issue691.dom3.org"));
++ assertTrue(updated.getVirAttrs().iterator().next().getValues().contains("test@issue691.dom4.org"));
++ } finally {
++ try {
++ resourceService.delete(res);
++ } catch (Exception ignore) {
++ // ignore
++ }
++ }
++ }
++
+}