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 2013/01/03 11:13:01 UTC
svn commit: r1428262 - in /syncope/branches/1_0_X/core/src:
main/java/org/apache/syncope/core/persistence/beans/
main/java/org/apache/syncope/core/propagation/
main/java/org/apache/syncope/core/util/
test/java/org/apache/syncope/core/rest/
Author: fmartelli
Date: Thu Jan 3 10:13:01 2013
New Revision: 1428262
URL: http://svn.apache.org/viewvc?rev=1428262&view=rev
Log:
Fixes SYNCOPE-260
Added:
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/persistence/beans/Attributable.java (with props)
Modified:
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/persistence/beans/AbstractAttributable.java
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationManager.java
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/SchemaMappingUtil.java
syncope/branches/1_0_X/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
Modified: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/persistence/beans/AbstractAttributable.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/persistence/beans/AbstractAttributable.java?rev=1428262&r1=1428261&r2=1428262&view=diff
==============================================================================
--- syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/persistence/beans/AbstractAttributable.java (original)
+++ syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/persistence/beans/AbstractAttributable.java Thu Jan 3 10:13:01 2013
@@ -21,11 +21,10 @@ package org.apache.syncope.core.persiste
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.List;
import java.util.Map;
import java.util.Set;
-public abstract class AbstractAttributable extends AbstractBaseBean {
+public abstract class AbstractAttributable extends AbstractBaseBean implements Attributable {
private static final long serialVersionUID = -4801685541488201119L;
@@ -108,32 +107,6 @@ public abstract class AbstractAttributab
return map;
}
- public abstract Long getId();
-
- public abstract <T extends AbstractAttr> boolean addAttribute(T attribute);
-
- public abstract <T extends AbstractAttr> boolean removeAttribute(T attribute);
-
- public abstract List<? extends AbstractAttr> getAttributes();
-
- public abstract void setAttributes(List<? extends AbstractAttr> attributes);
-
- public abstract <T extends AbstractDerAttr> boolean addDerivedAttribute(T derivedAttribute);
-
- public abstract <T extends AbstractDerAttr> boolean removeDerivedAttribute(T derivedAttribute);
-
- public abstract List<? extends AbstractDerAttr> getDerivedAttributes();
-
- public abstract void setDerivedAttributes(List<? extends AbstractDerAttr> derivedAttributes);
-
- public abstract <T extends AbstractVirAttr> boolean addVirtualAttribute(T virtualAttributes);
-
- public abstract <T extends AbstractVirAttr> boolean removeVirtualAttribute(T virtualAttribute);
-
- public abstract List<? extends AbstractVirAttr> getVirtualAttributes();
-
- public abstract void setVirtualAttributes(List<? extends AbstractVirAttr> virtualAttributes);
-
protected abstract Set<ExternalResource> resources();
public boolean addResource(final ExternalResource resource) {
Added: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/persistence/beans/Attributable.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/persistence/beans/Attributable.java?rev=1428262&view=auto
==============================================================================
--- syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/persistence/beans/Attributable.java (added)
+++ syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/persistence/beans/Attributable.java Thu Jan 3 10:13:01 2013
@@ -0,0 +1,71 @@
+/*
+ * 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.persistence.beans;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Entity interface. It has been introduced to specify a dynamic proxy for AbstractAttributable instances in order to
+ * manage virtual attributes during propagation.
+ */
+public interface Attributable {
+
+ <T extends AbstractAttr> T getAttribute(final String schemaName);
+
+ <T extends AbstractDerAttr> T getDerivedAttribute(final String derivedSchemaName);
+
+ <T extends AbstractVirAttr> T getVirtualAttribute(final String virtualSchemaName);
+
+ Long getId();
+
+ <T extends AbstractAttr> boolean addAttribute(T attribute);
+
+ <T extends AbstractAttr> boolean removeAttribute(T attribute);
+
+ List<? extends AbstractAttr> getAttributes();
+
+ void setAttributes(List<? extends AbstractAttr> attributes);
+
+ <T extends AbstractDerAttr> boolean addDerivedAttribute(T derivedAttribute);
+
+ <T extends AbstractDerAttr> boolean removeDerivedAttribute(T derivedAttribute);
+
+ List<? extends AbstractDerAttr> getDerivedAttributes();
+
+ void setDerivedAttributes(List<? extends AbstractDerAttr> derivedAttributes);
+
+ <T extends AbstractVirAttr> boolean addVirtualAttribute(T virtualAttributes);
+
+ <T extends AbstractVirAttr> boolean removeVirtualAttribute(T virtualAttribute);
+
+ List<? extends AbstractVirAttr> getVirtualAttributes();
+
+ void setVirtualAttributes(List<? extends AbstractVirAttr> virtualAttributes);
+
+ boolean addResource(final ExternalResource resource);
+
+ boolean removeResource(final ExternalResource resource);
+
+ Set<ExternalResource> getResources();
+
+ Set<String> getResourceNames();
+
+ void setResources(final Set<ExternalResource> resources);
+}
Propchange: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/persistence/beans/Attributable.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/persistence/beans/Attributable.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/persistence/beans/Attributable.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationManager.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationManager.java?rev=1428262&r1=1428261&r2=1428262&view=diff
==============================================================================
--- syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationManager.java (original)
+++ syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationManager.java Thu Jan 3 10:13:01 2013
@@ -19,10 +19,15 @@
package org.apache.syncope.core.propagation;
import java.io.PrintWriter;
+import java.io.Serializable;
import java.io.StringWriter;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -34,8 +39,9 @@ import org.apache.syncope.client.mod.Att
import org.apache.syncope.client.to.AttributeTO;
import org.apache.syncope.core.init.ConnInstanceLoader;
import org.apache.syncope.core.persistence.beans.AbstractAttrValue;
-import org.apache.syncope.core.persistence.beans.AbstractAttributable;
import org.apache.syncope.core.persistence.beans.AbstractSchema;
+import org.apache.syncope.core.persistence.beans.AbstractVirAttr;
+import org.apache.syncope.core.persistence.beans.Attributable;
import org.apache.syncope.core.persistence.beans.ExternalResource;
import org.apache.syncope.core.persistence.beans.PropagationTask;
import org.apache.syncope.core.persistence.beans.SchemaMapping;
@@ -284,7 +290,15 @@ public class PropagationManager {
localPropByRes.get(PropagationOperation.DELETE).removeAll(syncResourceNames);
}
- return provision(user, password, enable, false, localPropByRes);
+ // Provide a proxy handler in order to take into consideration all the info about virtual attributes to
+ // be removed/updated as well.
+
+ final Attributable handleObject = (Attributable) Proxy.newProxyInstance(
+ Attributable.class.getClassLoader(),
+ new Class<?>[]{Attributable.class, Serializable.class},
+ new AttributableHandler(user, vAttrsToBeRemoved, vAttrsToBeUpdated));
+
+ return provision(handleObject, password, enable, false, localPropByRes);
}
/**
@@ -335,24 +349,29 @@ public class PropagationManager {
* @return account link + prepare attributes
* @throws ClassNotFoundException if schema type for given mapping does not exists in current class loader
*/
- private Map.Entry<String, Attribute> prepareAttribute(final SchemaMapping mapping, final SyncopeUser user,
- final String password)
+ private Map.Entry<String, Attribute> prepareAttribute(
+ final SchemaMapping mapping, final Attributable user, final String password)
throws ClassNotFoundException {
- final List<AbstractAttributable> attributables = new ArrayList<AbstractAttributable>();
+ // Retrieve attributable ...
+ final Attributable attributable = user instanceof Proxy
+ ? ((AttributableHandler) Proxy.getInvocationHandler(user)).getObject()
+ : user;
+
+ final List<Attributable> attributables = new ArrayList<Attributable>();
switch (mapping.getIntMappingType().getAttributableType()) {
case USER:
attributables.addAll(Collections.singleton(user));
break;
case ROLE:
- final List<Membership> memberships = user.getMemberships();
+ final List<Membership> memberships = ((SyncopeUser) attributable).getMemberships();
for (Membership membership : memberships) {
attributables.add(membership.getSyncopeRole());
}
break;
case MEMBERSHIP:
- attributables.addAll(user.getMemberships());
+ attributables.addAll(((SyncopeUser) attributable).getMemberships());
break;
default:
}
@@ -416,11 +435,16 @@ public class PropagationManager {
* @param resource target resource
* @return account link + prepared attributes
*/
- private Map.Entry<String, Set<Attribute>> prepareAttributes(final SyncopeUser user, final String password,
+ private Map.Entry<String, Set<Attribute>> prepareAttributes(final Attributable user, final String password,
final Boolean enable, final ExternalResource resource) {
+ // Retrieve attributable ...
+ final Attributable attributable = user instanceof Proxy
+ ? ((AttributableHandler) Proxy.getInvocationHandler(user)).getObject()
+ : user;
+
LOG.debug("Preparing resource attributes for {} on resource {} with attributes {}",
- new Object[]{user, resource, user.getAttributes()});
+ new Object[]{attributable, resource, attributable.getAttributes()});
Set<Attribute> attributes = new HashSet<Attribute>();
String accountId = null;
@@ -437,8 +461,8 @@ public class PropagationManager {
}
if (preparedAttribute.getValue() != null) {
- final Attribute alreadyAdded = AttributeUtil.find(preparedAttribute.getValue().getName(),
- attributes);
+ final Attribute alreadyAdded =
+ AttributeUtil.find(preparedAttribute.getValue().getName(), attributes);
if (alreadyAdded == null) {
attributes.add(preparedAttribute.getValue());
@@ -464,7 +488,7 @@ public class PropagationManager {
}
// Evaluate AccountLink expression
- String evalAccountLink = jexlUtil.evaluate(resource.getAccountLink(), user);
+ String evalAccountLink = jexlUtil.evaluate(resource.getAccountLink(), (SyncopeUser) attributable);
// AccountId must be propagated. It could be a simple attribute for
// the target resource or the key (depending on the accountLink)
@@ -498,10 +522,18 @@ public class PropagationManager {
* @param propByRes operation to be performed per resource
* @return list of propagation tasks created
*/
- protected List<PropagationTask> provision(final SyncopeUser user, final String password, final Boolean enable,
+ protected List<PropagationTask> provision(
+ final Attributable user,
+ final String password,
+ final Boolean enable,
final boolean deleteOnResource, final PropagationByResource propByRes) {
- LOG.debug("Provisioning with user {}:\n{}", user, propByRes);
+ // Retrieve attributable ...
+ final Attributable attributable = user instanceof Proxy
+ ? ((AttributableHandler) Proxy.getInvocationHandler(user)).getObject()
+ : user;
+
+ LOG.debug("Provisioning with user {}:\n{}", attributable, propByRes);
// Avoid duplicates - see javadoc
propByRes.purge();
@@ -521,13 +553,14 @@ public class PropagationManager {
PropagationTask task = new PropagationTask();
task.setResource(resource);
if (!deleteOnResource) {
- task.setSyncopeUser(user);
+ task.setSyncopeUser((SyncopeUser) attributable);
}
task.setPropagationOperation(operation);
task.setPropagationMode(resource.getPropagationMode());
task.setOldAccountId(propByRes.getOldAccountId(resource.getName()));
Map.Entry<String, Set<Attribute>> preparedAttrs = prepareAttributes(user, password, enable, resource);
+
task.setAccountId(preparedAttrs.getKey());
task.setAttributes(preparedAttrs.getValue());
@@ -715,19 +748,18 @@ public class PropagationManager {
LOG.debug("{} not found on external resource: ignoring delete", task.getAccountId());
} else {
/*
- * We must choose here whether to
- * a. actually delete the provided user from the external resource
- * b. just update the provided user data onto the external resource
+ * We must choose here whether to a. actually delete the provided user from the external
+ * resource b. just update the provided user data onto the external resource
*
* (a) happens when either there is no user associated with the PropagationTask (this takes
* place when the task is generated via UserController.delete()) or the provided updated
* user hasn't the current resource assigned (when the task is generated via
* UserController.update()).
*
- * (b) happens when the provided updated user does have the current resource assigned
- * (when the task is generated via UserController.update()): this basically means that
- * before such update, this user used to have the current resource assigned by more than
- * one mean (for example, two different memberships with the same resource).
+ * (b) happens when the provided updated user does have the current resource assigned (when
+ * the task is generated via UserController.update()): this basically means that before such
+ * update, this user used to have the current resource assigned by more than one mean (for
+ * example, two different memberships with the same resource).
*/
SyncopeUser user = null;
@@ -860,4 +892,55 @@ public class PropagationManager {
return null;
}
}
+
+ public static class AttributableHandler implements InvocationHandler {
+
+ private Attributable object;
+
+ private Set<String> vAttrsToBeRemoved;
+
+ private Map<String, AttributeMod> vAttrsToBeUpdated;
+
+ public AttributableHandler(
+ final Attributable object,
+ final Set<String> vAttrsToBeRemoved,
+ final Set<AttributeMod> vAttrsToBeUpdated) {
+ this.object = object;
+ this.vAttrsToBeRemoved = vAttrsToBeRemoved;
+
+ if (vAttrsToBeUpdated != null) {
+ this.vAttrsToBeUpdated = new HashMap<String, AttributeMod>(vAttrsToBeUpdated.size());
+
+ for (AttributeMod attrMod : vAttrsToBeUpdated) {
+ this.vAttrsToBeUpdated.put(attrMod.getSchema(), attrMod);
+ }
+ } else {
+ this.vAttrsToBeUpdated = Collections.EMPTY_MAP;
+ }
+ }
+
+ @Override
+ public Object invoke(final Object proxy, final Method method, final Object[] args)
+ throws Throwable {
+ if ("getVirtualAttribute".equals(method.getName()) && args.length == 1 && (args[0] instanceof String)) {
+ final AbstractVirAttr attr = object.getVirtualAttribute((String) args[0]);
+
+ if (vAttrsToBeUpdated.containsKey((String) args[0])) {
+ attr.setValues(vAttrsToBeUpdated.get((String) args[0]).getValuesToBeAdded());
+ } else if (vAttrsToBeRemoved.contains((String) args[0])) {
+ attr.getValues().clear();
+ } else {
+ throw new RuntimeException("Virtual attribute has not to be updated");
+ }
+
+ return attr;
+ } else {
+ return method.invoke(object, args);
+ }
+ }
+
+ public Attributable getObject() {
+ return object;
+ }
+ }
}
Modified: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/SchemaMappingUtil.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/SchemaMappingUtil.java?rev=1428262&r1=1428261&r2=1428262&view=diff
==============================================================================
--- syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/SchemaMappingUtil.java (original)
+++ syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/SchemaMappingUtil.java Thu Jan 3 10:13:01 2013
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.core.util;
+import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -33,6 +34,7 @@ import org.apache.syncope.core.persisten
import org.apache.syncope.core.persistence.beans.AbstractDerAttr;
import org.apache.syncope.core.persistence.beans.AbstractSchema;
import org.apache.syncope.core.persistence.beans.AbstractVirAttr;
+import org.apache.syncope.core.persistence.beans.Attributable;
import org.apache.syncope.core.persistence.beans.SchemaMapping;
import org.apache.syncope.core.persistence.beans.membership.MDerSchema;
import org.apache.syncope.core.persistence.beans.membership.MSchema;
@@ -46,11 +48,13 @@ import org.apache.syncope.core.persisten
import org.apache.syncope.core.persistence.beans.user.USchema;
import org.apache.syncope.core.persistence.beans.user.UVirSchema;
import org.apache.syncope.core.persistence.dao.SchemaDAO;
+import org.apache.syncope.core.propagation.PropagationManager.AttributableHandler;
import org.apache.syncope.types.IntMappingType;
import org.identityconnectors.framework.common.objects.OperationalAttributes;
import org.identityconnectors.framework.common.objects.Uid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import sun.security.util.Password;
public class SchemaMappingUtil {
@@ -135,7 +139,7 @@ public class SchemaMappingUtil {
* @return schema and attribute values.
*/
public static Map.Entry<AbstractSchema, List<AbstractAttrValue>> getIntValues(final SchemaMapping mapping,
- final List<AbstractAttributable> attributables, final String password, final SchemaDAO schemaDAO) {
+ final List<Attributable> attributables, final String password, final SchemaDAO schemaDAO) {
LOG.debug("Get attributes for '{}' and mapping type '{}'", attributables, mapping.getIntMappingType());
@@ -150,7 +154,7 @@ public class SchemaMappingUtil {
schema = schemaDAO.find(mapping.getIntAttrName(), SchemaMappingUtil.getIntMappingTypeClass(mapping.
getIntMappingType()));
- for (AbstractAttributable attributable : attributables) {
+ for (Attributable attributable : attributables) {
final AbstractAttr attr = attributable.getAttribute(mapping.getIntAttrName());
if (attr != null && attr.getValues() != null) {
@@ -169,7 +173,7 @@ public class SchemaMappingUtil {
case RoleVirtualSchema:
case MembershipVirtualSchema:
- for (AbstractAttributable attributable : attributables) {
+ for (Attributable attributable : attributables) {
AbstractVirAttr virAttr = attributable.getVirtualAttribute(mapping.getIntAttrName());
if (virAttr != null && virAttr.getValues() != null) {
@@ -189,7 +193,7 @@ public class SchemaMappingUtil {
case UserDerivedSchema:
case RoleDerivedSchema:
case MembershipDerivedSchema:
- for (AbstractAttributable attributable : attributables) {
+ for (Attributable attributable : attributables) {
AbstractDerAttr derAttr = attributable.getDerivedAttribute(mapping.getIntAttrName());
if (derAttr != null) {
@@ -205,15 +209,19 @@ public class SchemaMappingUtil {
break;
case Username:
- for (AbstractAttributable attributable : attributables) {
+ for (Attributable attributable : attributables) {
AbstractAttrValue attrValue = new UAttrValue();
- attrValue.setStringValue(((SyncopeUser) attributable).getUsername());
+ SyncopeUser user = attributable instanceof Proxy?
+ (SyncopeUser)((AttributableHandler)Proxy.getInvocationHandler(attributable)).getObject():
+ (SyncopeUser)attributable;
+
+ attrValue.setStringValue(user.getUsername());
values.add(attrValue);
}
break;
case SyncopeUserId:
- for (AbstractAttributable attributable : attributables) {
+ for (Attributable attributable : attributables) {
AbstractAttrValue attrValue = new UAttrValue();
attrValue.setStringValue(attributable.getId().toString());
values.add(attrValue);
Modified: syncope/branches/1_0_X/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java?rev=1428262&r1=1428261&r2=1428262&view=diff
==============================================================================
--- syncope/branches/1_0_X/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java (original)
+++ syncope/branches/1_0_X/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java Thu Jan 3 10:13:01 2013
@@ -38,9 +38,7 @@ import org.springframework.http.HttpStat
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.client.HttpStatusCodeException;
import org.apache.syncope.client.http.PreemptiveAuthHttpRequestFactory;
-import org.apache.syncope.client.mod.AttributeMod;
-import org.apache.syncope.client.mod.MembershipMod;
-import org.apache.syncope.client.mod.UserMod;
+import org.apache.syncope.client.mod.*;
import org.apache.syncope.client.to.AttributeTO;
import org.apache.syncope.client.search.AttributeCond;
import org.apache.syncope.client.search.SyncopeUserCond;
@@ -576,8 +574,7 @@ public class UserTestITCase extends Abst
assertEquals(maxTaskExecutions, taskTO.getExecutions().size());
// 3. verify password
- Boolean verify = restTemplate.
- getForObject(BASE_URL + "user/verifyPassword/{username}.json?password=password123",
+ Boolean verify = restTemplate.getForObject(BASE_URL + "user/verifyPassword/{username}.json?password=password123",
Boolean.class, newUserTO.getUsername());
assertTrue(verify);
@@ -902,8 +899,7 @@ public class UserTestITCase extends Abst
assertNotNull(user);
}
- users = Arrays.
- asList(restTemplate.getForObject(BASE_URL + "user/list/{page}/{size}.json", UserTO[].class, 2, 2));
+ users = Arrays.asList(restTemplate.getForObject(BASE_URL + "user/list/{page}/{size}.json", UserTO[].class, 2, 2));
assertNotNull(users);
assertFalse(users.isEmpty());
@@ -2037,4 +2033,50 @@ public class UserTestITCase extends Abst
}
assertNotNull(sce);
}
+
+ @Test
+ public void issueSYNCOPE260() {
+ // 1. create user with SOAP resource, succesfully propagated
+ UserTO userTO = getSampleTO("syncope260@apache.org");
+ userTO.addResource("ws-target-resource-2");
+
+ userTO = restTemplate.postForObject(BASE_URL + "user/create", userTO, UserTO.class);
+ assertNotNull(userTO);
+ assertFalse(userTO.getPropagationTOs().isEmpty());
+ assertEquals("ws-target-resource-2", userTO.getPropagationTOs().get(0).getResourceName());
+ assertEquals(PropagationTaskExecStatus.SUBMITTED, userTO.getPropagationTOs().get(0).getStatus());
+
+ // 3. try to find this user on the external SOAP resource
+ ConnObjectTO connObjectTO = restTemplate.getForObject(
+ BASE_URL + "/resource/{resourceName}/read/{objectId}.json",
+ ConnObjectTO.class, "ws-target-resource-2", userTO.getUsername());
+ assertNotNull(connObjectTO);
+ assertEquals("virtualvalue", connObjectTO.getAttributeMap().get("NAME").getValues().get(0));
+
+ UserMod userMod = new UserMod();
+ userMod.setId(userTO.getId());
+
+ AttributeMod attrMod = new AttributeMod();
+ attrMod.setSchema("surname");
+ attrMod.addValueToBeRemoved("Surname");
+ attrMod.addValueToBeAdded("Surname2");
+
+ userMod.addAttributeToBeUpdated(attrMod);
+
+ userTO = restTemplate.postForObject(BASE_URL + "user/update", userMod, UserTO.class);
+ assertNotNull(userTO);
+ assertFalse(userTO.getPropagationTOs().isEmpty());
+ assertEquals("ws-target-resource-2", userTO.getPropagationTOs().get(0).getResourceName());
+ assertEquals(PropagationTaskExecStatus.SUBMITTED, userTO.getPropagationTOs().get(0).getStatus());
+
+ connObjectTO = restTemplate.getForObject(
+ BASE_URL + "/resource/{resourceName}/read/{objectId}.json",
+ ConnObjectTO.class, "ws-target-resource-2", userTO.getUsername());
+ assertNotNull(connObjectTO);
+ assertEquals("Surname2", connObjectTO.getAttributeMap().get("SURNAME").getValues().get(0));
+
+ // attribute "name" mapped on virtual attribute "virtualdata" shouldn't be changed
+ assertFalse(connObjectTO.getAttributeMap().get("NAME").getValues().isEmpty());
+ assertEquals("virtualvalue", connObjectTO.getAttributeMap().get("NAME").getValues().get(0));
+ }
}