You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2016/01/26 13:04:44 UTC

[13/21] syncope git commit: [SYNCOPE-152] Moving console IT under fit/core-reference in order to speed-up the total build time

http://git-wip-us.apache.org/repos/asf/syncope/blob/17d5d892/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java
new file mode 100644
index 0000000..dd8e7c1
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java
@@ -0,0 +1,386 @@
+/*
+ * 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;
+
+import org.apache.syncope.fit.ActivitiDetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.security.AccessControlException;
+import java.util.Map;
+import java.util.Set;
+import javax.ws.rs.core.GenericType;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.patch.BooleanReplacePatchItem;
+import org.apache.syncope.common.lib.patch.MembershipPatch;
+import org.apache.syncope.common.lib.patch.PasswordPatch;
+import org.apache.syncope.common.lib.patch.StringPatchItem;
+import org.apache.syncope.common.lib.patch.StringReplacePatchItem;
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.ProvisioningResult;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.to.WorkflowFormPropertyTO;
+import org.apache.syncope.common.lib.to.WorkflowFormTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.PatchOperation;
+import org.apache.syncope.common.rest.api.service.ResourceService;
+import org.apache.syncope.common.rest.api.service.UserSelfService;
+import org.apache.syncope.common.rest.api.service.UserService;
+import org.apache.syncope.fit.AbstractITCase;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class UserSelfITCase extends AbstractITCase {
+
+    @Test
+    public void selfRegistrationAllowed() {
+        assertTrue(syncopeService.info().isSelfRegAllowed());
+    }
+
+    @Test
+    public void create() {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(syncopeService));
+
+        // 1. self-registration as admin: failure
+        try {
+            userSelfService.create(UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org"), true);
+            fail();
+        } catch (AccessControlException e) {
+            assertNotNull(e);
+        }
+
+        // 2. self-registration as anonymous: works
+        SyncopeClient anonClient = clientFactory.create();
+        UserTO self = anonClient.getService(UserSelfService.class).
+                create(UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org"), true).
+                readEntity(new GenericType<ProvisioningResult<UserTO>>() {
+                }).getAny();
+        assertNotNull(self);
+        assertEquals("createApproval", self.getStatus());
+    }
+
+    @Test
+    public void createAndApprove() {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(syncopeService));
+
+        // self-create user with membership: goes 'createApproval' with resources and membership but no propagation
+        UserTO userTO = UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org");
+        userTO.getMemberships().add(new MembershipTO.Builder().group(3L).build());
+        userTO.getResources().add(RESOURCE_NAME_TESTDB);
+
+        SyncopeClient anonClient = clientFactory.create();
+        userTO = anonClient.getService(UserSelfService.class).
+                create(userTO, true).
+                readEntity(new GenericType<ProvisioningResult<UserTO>>() {
+                }).getAny();
+        assertNotNull(userTO);
+        assertEquals("createApproval", userTO.getStatus());
+        assertFalse(userTO.getMemberships().isEmpty());
+        assertFalse(userTO.getResources().isEmpty());
+
+        try {
+            resourceService.readConnObject(RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), userTO.getKey());
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+        }
+
+        // now approve and verify that propagation has happened
+        WorkflowFormTO form = userWorkflowService.getFormForUser(userTO.getKey());
+        form = userWorkflowService.claimForm(form.getTaskId());
+        Map<String, WorkflowFormPropertyTO> props = form.getPropertyMap();
+        props.get("approve").setValue(Boolean.TRUE.toString());
+        form.getProperties().clear();
+        form.getProperties().addAll(props.values());
+        userTO = userWorkflowService.submitForm(form);
+        assertNotNull(userTO);
+        assertEquals("active", userTO.getStatus());
+        assertNotNull(resourceService.readConnObject(RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), userTO.getKey()));
+    }
+
+    @Test
+    public void read() {
+        UserService userService2 = clientFactory.create("rossini", ADMIN_PWD).getService(UserService.class);
+
+        try {
+            userService2.read(1L);
+            fail();
+        } catch (AccessControlException e) {
+            assertNotNull(e);
+        }
+
+        Pair<Map<String, Set<String>>, UserTO> self = clientFactory.create("rossini", ADMIN_PWD).self();
+        assertEquals("rossini", self.getValue().getUsername());
+    }
+
+    @Test
+    public void updateWithoutApproval() {
+        // 1. create user as admin
+        UserTO created = createUser(UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org")).getAny();
+        assertNotNull(created);
+        assertFalse(created.getUsername().endsWith("XX"));
+
+        // 2. self-update (username) - works
+        UserPatch userPatch = new UserPatch();
+        userPatch.setKey(created.getKey());
+        userPatch.setUsername(new StringReplacePatchItem.Builder().value(created.getUsername() + "XX").build());
+
+        SyncopeClient authClient = clientFactory.create(created.getUsername(), "password123");
+        UserTO updated = authClient.getService(UserSelfService.class).update(userPatch).
+                readEntity(new GenericType<ProvisioningResult<UserTO>>() {
+                }).getAny();
+        assertNotNull(updated);
+        assertEquals(ActivitiDetector.isActivitiEnabledForUsers(syncopeService)
+                ? "active" : "created", updated.getStatus());
+        assertTrue(updated.getUsername().endsWith("XX"));
+    }
+
+    @Test
+    public void updateWithApproval() {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(syncopeService));
+
+        // 1. create user as admin
+        UserTO created = createUser(UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org")).getAny();
+        assertNotNull(created);
+        assertFalse(created.getUsername().endsWith("XX"));
+
+        // 2. self-update (username + memberships + resource) - works but needs approval
+        UserPatch userPatch = new UserPatch();
+        userPatch.setKey(created.getKey());
+        userPatch.setUsername(new StringReplacePatchItem.Builder().value(created.getUsername() + "XX").build());
+        userPatch.getMemberships().add(new MembershipPatch.Builder().
+                operation(PatchOperation.ADD_REPLACE).
+                membershipTO(new MembershipTO.Builder().group(7L).build()).
+                build());
+        userPatch.getResources().add(new StringPatchItem.Builder().
+                operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_TESTDB).build());
+        userPatch.setPassword(new PasswordPatch.Builder().
+                value("newPassword123").onSyncope(false).resource(RESOURCE_NAME_TESTDB).build());
+
+        SyncopeClient authClient = clientFactory.create(created.getUsername(), "password123");
+        UserTO updated = authClient.getService(UserSelfService.class).update(userPatch).
+                readEntity(new GenericType<ProvisioningResult<UserTO>>() {
+                }).getAny();
+        assertNotNull(updated);
+        assertEquals("updateApproval", updated.getStatus());
+        assertFalse(updated.getUsername().endsWith("XX"));
+        assertTrue(updated.getMemberships().isEmpty());
+
+        // no propagation happened
+        assertTrue(updated.getResources().isEmpty());
+        try {
+            resourceService.readConnObject(RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), updated.getKey());
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+        }
+
+        // 3. approve self-update as admin
+        WorkflowFormTO form = userWorkflowService.getFormForUser(updated.getKey());
+        form = userWorkflowService.claimForm(form.getTaskId());
+        Map<String, WorkflowFormPropertyTO> props = form.getPropertyMap();
+        props.get("approve").setValue(Boolean.TRUE.toString());
+        form.getProperties().clear();
+        form.getProperties().addAll(props.values());
+        updated = userWorkflowService.submitForm(form);
+        assertNotNull(updated);
+        assertEquals("active", updated.getStatus());
+        assertTrue(updated.getUsername().endsWith("XX"));
+        assertEquals(1, updated.getMemberships().size());
+
+        // check that propagation also happened
+        assertTrue(updated.getResources().contains(RESOURCE_NAME_TESTDB));
+        assertNotNull(resourceService.readConnObject(RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), updated.getKey()));
+    }
+
+    @Test
+    public void delete() {
+        UserTO created = createUser(UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org")).getAny();
+        assertNotNull(created);
+
+        SyncopeClient authClient = clientFactory.create(created.getUsername(), "password123");
+        UserTO deleted = authClient.getService(UserSelfService.class).delete().readEntity(
+                new GenericType<ProvisioningResult<UserTO>>() {
+        }).getAny();
+        assertNotNull(deleted);
+        assertEquals(ActivitiDetector.isActivitiEnabledForUsers(syncopeService)
+                ? "deleteApproval" : null, deleted.getStatus());
+    }
+
+    @Test
+    public void issueSYNCOPE373() {
+        UserTO userTO = adminClient.self().getValue();
+        assertEquals(ADMIN_UNAME, userTO.getUsername());
+    }
+
+    @Test
+    public void passwordReset() {
+        // 0. ensure that password request DOES require security question
+        configurationService.set(attrTO("passwordReset.securityQuestion", "true"));
+
+        // 1. create an user with security question and answer
+        UserTO user = UserITCase.getUniqueSampleTO("pwdReset@syncope.apache.org");
+        user.setSecurityQuestion(1L);
+        user.setSecurityAnswer("Rossi");
+        user.getResources().add(RESOURCE_NAME_TESTDB);
+        createUser(user);
+
+        // verify propagation (including password) on external db
+        JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+        String pwdOnResource = jdbcTemplate.queryForObject("SELECT password FROM test WHERE id=?", String.class,
+                user.getUsername());
+        assertTrue(StringUtils.isNotBlank(pwdOnResource));
+
+        // 2. verify that new user is able to authenticate
+        SyncopeClient authClient = clientFactory.create(user.getUsername(), "password123");
+        UserTO read = authClient.self().getValue();
+        assertNotNull(read);
+
+        // 3. request password reset (as anonymous) providing the expected security answer
+        SyncopeClient anonClient = clientFactory.create();
+        try {
+            anonClient.getService(UserSelfService.class).requestPasswordReset(user.getUsername(), "WRONG");
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidSecurityAnswer, e.getType());
+        }
+        anonClient.getService(UserSelfService.class).requestPasswordReset(user.getUsername(), "Rossi");
+
+        // 4. get token (normally sent via e-mail, now reading as admin)
+        String token = userService.read(read.getKey()).getToken();
+        assertNotNull(token);
+
+        // 5. confirm password reset
+        try {
+            anonClient.getService(UserSelfService.class).confirmPasswordReset("WRONG TOKEN", "newPassword");
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+            assertTrue(e.getMessage().contains("WRONG TOKEN"));
+        }
+        anonClient.getService(UserSelfService.class).confirmPasswordReset(token, "newPassword123");
+
+        // 6. verify that password was reset and token removed
+        authClient = clientFactory.create(user.getUsername(), "newPassword123");
+        read = authClient.self().getValue();
+        assertNotNull(read);
+        assertNull(read.getToken());
+
+        // 7. verify that password was changed on external resource
+        String newPwdOnResource = jdbcTemplate.queryForObject("SELECT password FROM test WHERE id=?", String.class,
+                user.getUsername());
+        assertTrue(StringUtils.isNotBlank(newPwdOnResource));
+        assertNotEquals(pwdOnResource, newPwdOnResource);
+    }
+
+    @Test
+    public void passwordResetWithoutSecurityQuestion() {
+        // 0. disable security question for password reset
+        configurationService.set(attrTO("passwordReset.securityQuestion", "false"));
+
+        // 1. create an user with security question and answer
+        UserTO user = UserITCase.getUniqueSampleTO("pwdResetNoSecurityQuestion@syncope.apache.org");
+        createUser(user);
+
+        // 2. verify that new user is able to authenticate
+        SyncopeClient authClient = clientFactory.create(user.getUsername(), "password123");
+        UserTO read = authClient.self().getValue();
+        assertNotNull(read);
+
+        // 3. request password reset (as anonymous) with no security answer
+        SyncopeClient anonClient = clientFactory.create();
+        anonClient.getService(UserSelfService.class).requestPasswordReset(user.getUsername(), null);
+
+        // 4. get token (normally sent via e-mail, now reading as admin)
+        String token = userService.read(read.getKey()).getToken();
+        assertNotNull(token);
+
+        // 5. confirm password reset
+        try {
+            anonClient.getService(UserSelfService.class).confirmPasswordReset("WRONG TOKEN", "newPassword");
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+            assertTrue(e.getMessage().contains("WRONG TOKEN"));
+        }
+        anonClient.getService(UserSelfService.class).confirmPasswordReset(token, "newPassword123");
+
+        // 6. verify that password was reset and token removed
+        authClient = clientFactory.create(user.getUsername(), "newPassword123");
+        read = authClient.self().getValue();
+        assertNotNull(read);
+        assertNull(read.getToken());
+
+        // 7. re-enable security question for password reset
+        configurationService.set(attrTO("passwordReset.securityQuestion", "true"));
+    }
+
+    @Test
+    public void mustChangePassword() {
+        // PRE: reset vivaldi's password
+        UserPatch userPatch = new UserPatch();
+        userPatch.setKey(3L);
+        userPatch.setPassword(new PasswordPatch.Builder().value("password321").build());
+        userService.update(userPatch);
+
+        // 0. access as vivaldi -> succeed
+        SyncopeClient vivaldiClient = clientFactory.create("vivaldi", "password321");
+        Pair<Map<String, Set<String>>, UserTO> self = vivaldiClient.self();
+        assertFalse(self.getRight().isMustChangePassword());
+
+        // 1. update user vivaldi (3) requirig password update
+        userPatch = new UserPatch();
+        userPatch.setKey(3L);
+        userPatch.setMustChangePassword(new BooleanReplacePatchItem.Builder().value(true).build());
+        UserTO vivaldi = updateUser(userPatch).getAny();
+        assertTrue(vivaldi.isMustChangePassword());
+
+        // 2. attempt to access -> fail
+        try {
+            vivaldiClient.getService(ResourceService.class).list();
+            fail();
+        } catch (AccessControlException e) {
+            assertNotNull(e);
+            assertEquals("Please change your password first", e.getMessage());
+        }
+
+        // 3. change password
+        vivaldiClient.getService(UserSelfService.class).changePassword("password123");
+
+        // 4. verify it worked
+        self = clientFactory.create("vivaldi", "password123").self();
+        assertFalse(self.getRight().isMustChangePassword());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/17d5d892/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserWorkflowITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserWorkflowITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserWorkflowITCase.java
new file mode 100644
index 0000000..18b3cbf
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserWorkflowITCase.java
@@ -0,0 +1,309 @@
+/*
+ * 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;
+
+import org.apache.syncope.fit.ActivitiDetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.patch.PasswordPatch;
+import org.apache.syncope.common.lib.patch.StringPatchItem;
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.ProvisioningResult;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.to.WorkflowFormPropertyTO;
+import org.apache.syncope.common.lib.to.WorkflowFormTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.PatchOperation;
+import org.apache.syncope.common.rest.api.service.UserWorkflowService;
+import org.apache.syncope.fit.AbstractITCase;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class UserWorkflowITCase extends AbstractITCase {
+
+    @Test
+    public void createWithReject() {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(syncopeService));
+
+        UserTO userTO = UserITCase.getUniqueSampleTO("createWithReject@syncope.apache.org");
+        userTO.getResources().add(RESOURCE_NAME_TESTDB);
+
+        // User with group 9 are defined in workflow as subject to approval
+        userTO.getMemberships().add(new MembershipTO.Builder().group(9L).build());
+
+        // 1. create user with group 9
+        userTO = createUser(userTO).getAny();
+        assertNotNull(userTO);
+        assertEquals(1, userTO.getMemberships().size());
+        assertEquals(9, userTO.getMemberships().get(0).getRightKey());
+        assertEquals("createApproval", userTO.getStatus());
+
+        // 2. request if there is any pending task for user just created
+        WorkflowFormTO form = userWorkflowService.getFormForUser(userTO.getKey());
+        assertNotNull(form);
+        assertNotNull(form.getUserKey());
+        assertEquals(userTO.getKey(), form.getUserKey(), 0);
+        assertNotNull(form.getTaskId());
+        assertNull(form.getOwner());
+
+        // 3. claim task as rossini, with role "User manager" granting entitlement to claim forms but not in group 7,
+        // designated for approval in workflow definition: fail
+        UserTO rossini = userService.read(1L);
+        if (!rossini.getRoles().contains("User manager")) {
+            UserPatch userPatch = new UserPatch();
+            userPatch.setKey(1L);
+            userPatch.getRoles().add(new StringPatchItem.Builder().
+                    operation(PatchOperation.ADD_REPLACE).value("User manager").build());
+            rossini = updateUser(userPatch).getAny();
+        }
+        assertTrue(rossini.getRoles().contains("User manager"));
+
+        UserWorkflowService userService2 = clientFactory.create("rossini", ADMIN_PWD).
+                getService(UserWorkflowService.class);
+        try {
+            userService2.claimForm(form.getTaskId());
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.Workflow, e.getType());
+        }
+
+        // 4. claim task from bellini, with role "User manager" and in group 7
+        UserWorkflowService userService3 = clientFactory.create("bellini", ADMIN_PWD).
+                getService(UserWorkflowService.class);
+        form = userService3.claimForm(form.getTaskId());
+        assertNotNull(form);
+        assertNotNull(form.getTaskId());
+        assertNotNull(form.getOwner());
+
+        // 5. reject user
+        Map<String, WorkflowFormPropertyTO> props = form.getPropertyMap();
+        props.get("approve").setValue(Boolean.FALSE.toString());
+        props.get("rejectReason").setValue("I don't like him.");
+        form.getProperties().clear();
+        form.getProperties().addAll(props.values());
+        userTO = userService3.submitForm(form);
+        assertNotNull(userTO);
+        assertEquals("rejected", userTO.getStatus());
+
+        // 6. check that rejected user was not propagated to external resource (SYNCOPE-364)
+        JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+        Exception exception = null;
+        try {
+            jdbcTemplate.queryForObject("SELECT id FROM test WHERE id=?",
+                    new String[] { userTO.getUsername() }, Integer.class);
+        } catch (EmptyResultDataAccessException e) {
+            exception = e;
+        }
+        assertNotNull(exception);
+    }
+
+    @Test
+    public void createWithApproval() {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(syncopeService));
+
+        // read forms *before* any operation
+        List<WorkflowFormTO> forms = userWorkflowService.getForms();
+        assertNotNull(forms);
+        int preForms = forms.size();
+
+        UserTO userTO = UserITCase.getUniqueSampleTO("createWithApproval@syncope.apache.org");
+        userTO.getResources().add(RESOURCE_NAME_TESTDB);
+
+        // User with group 9 are defined in workflow as subject to approval
+        userTO.getMemberships().add(new MembershipTO.Builder().group(9L).build());
+
+        // 1. create user with group 9 (and verify that no propagation occurred)
+        ProvisioningResult<UserTO> result = createUser(userTO);
+        assertNotNull(result);
+        userTO = result.getAny();
+        assertEquals(1, userTO.getMemberships().size());
+        assertEquals(9, userTO.getMemberships().get(0).getRightKey());
+        assertEquals("createApproval", userTO.getStatus());
+        assertEquals(Collections.singleton(RESOURCE_NAME_TESTDB), userTO.getResources());
+
+        assertTrue(result.getPropagationStatuses().isEmpty());
+
+        JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+
+        Exception exception = null;
+        try {
+            jdbcTemplate.queryForObject("SELECT id FROM test WHERE id=?",
+                    new String[] { userTO.getUsername() }, Integer.class);
+        } catch (EmptyResultDataAccessException e) {
+            exception = e;
+        }
+        assertNotNull(exception);
+
+        // 2. request if there is any pending form for user just created
+        forms = userWorkflowService.getForms();
+        assertNotNull(forms);
+        assertEquals(preForms + 1, forms.size());
+
+        WorkflowFormTO form = userWorkflowService.getFormForUser(userTO.getKey());
+        assertNotNull(form);
+        assertNotNull(form.getTaskId());
+        assertNull(form.getOwner());
+
+        // 4. claim task (from admin)
+        form = userWorkflowService.claimForm(form.getTaskId());
+        assertNotNull(form);
+        assertNotNull(form.getTaskId());
+        assertNotNull(form.getOwner());
+
+        // 5. approve user (and verify that propagation occurred)
+        Map<String, WorkflowFormPropertyTO> props = form.getPropertyMap();
+        props.get("approve").setValue(Boolean.TRUE.toString());
+        form.getProperties().clear();
+        form.getProperties().addAll(props.values());
+        userTO = userWorkflowService.submitForm(form);
+        assertNotNull(userTO);
+        assertEquals("active", userTO.getStatus());
+        assertEquals(Collections.singleton(RESOURCE_NAME_TESTDB), userTO.getResources());
+
+        exception = null;
+        try {
+            final String username = jdbcTemplate.queryForObject("SELECT id FROM test WHERE id=?", String.class,
+                    userTO.getUsername());
+            assertEquals(userTO.getUsername(), username);
+        } catch (EmptyResultDataAccessException e) {
+            exception = e;
+        }
+        assertNull(exception);
+
+        // 6. update user
+        UserPatch userPatch = new UserPatch();
+        userPatch.setKey(userTO.getKey());
+        userPatch.setPassword(new PasswordPatch.Builder().value("anotherPassword123").build());
+
+        userTO = updateUser(userPatch).getAny();
+        assertNotNull(userTO);
+    }
+
+    @Test
+    public void issueSYNCOPE15() {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(syncopeService));
+
+        // read forms *before* any operation
+        List<WorkflowFormTO> forms = userWorkflowService.getForms();
+        assertNotNull(forms);
+        int preForms = forms.size();
+
+        UserTO userTO = UserITCase.getUniqueSampleTO("issueSYNCOPE15@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getVirAttrs().clear();
+        userTO.getDerAttrs().clear();
+        userTO.getMemberships().clear();
+
+        // User with group 9 are defined in workflow as subject to approval
+        userTO.getMemberships().add(new MembershipTO.Builder().group(9L).build());
+
+        // 1. create user with group 9 (and verify that no propagation occurred)
+        userTO = createUser(userTO).getAny();
+        assertNotNull(userTO);
+        assertNotEquals(0L, userTO.getKey(), 0);
+        assertNotNull(userTO.getCreationDate());
+        assertNotNull(userTO.getCreator());
+        assertNotNull(userTO.getLastChangeDate());
+        assertNotNull(userTO.getLastModifier());
+        assertEquals(userTO.getCreationDate(), userTO.getLastChangeDate());
+
+        // 2. request if there is any pending form for user just created
+        forms = userWorkflowService.getForms();
+        assertEquals(preForms + 1, forms.size());
+
+        WorkflowFormTO form = userWorkflowService.getFormForUser(userTO.getKey());
+        assertNotNull(form);
+
+        // 3. first claim ny bellini ....
+        UserWorkflowService userService3 = clientFactory.create("bellini", ADMIN_PWD).
+                getService(UserWorkflowService.class);
+        form = userService3.claimForm(form.getTaskId());
+        assertNotNull(form);
+        assertNotNull(form.getTaskId());
+        assertNotNull(form.getOwner());
+
+        // 4. second claim task by admin
+        form = userWorkflowService.claimForm(form.getTaskId());
+        assertNotNull(form);
+
+        // 5. approve user
+        Map<String, WorkflowFormPropertyTO> props = form.getPropertyMap();
+        props.get("approve").setValue(Boolean.TRUE.toString());
+        form.getProperties().clear();
+        form.getProperties().addAll(props.values());
+
+        // 6. submit approve
+        userTO = userWorkflowService.submitForm(form);
+        assertNotNull(userTO);
+        assertEquals(preForms, userWorkflowService.getForms().size());
+        assertNull(userWorkflowService.getFormForUser(userTO.getKey()));
+
+        // 7. search approval into the history as well
+        forms = userWorkflowService.getFormsByName(userTO.getKey(), "Create approval");
+        assertFalse(forms.isEmpty());
+
+        int count = 0;
+        for (WorkflowFormTO hform : forms) {
+            if (form.getTaskId().equals(hform.getTaskId())) {
+                count++;
+
+                assertEquals("createApproval", hform.getKey());
+                assertNotNull(hform.getCreateTime());
+                assertNotNull(hform.getDueDate());
+                assertTrue(Boolean.parseBoolean(hform.getPropertyMap().get("approve").getValue()));
+                assertNull(hform.getPropertyMap().get("rejectReason").getValue());
+            }
+        }
+        assertEquals(1, count);
+
+        userService.delete(userTO.getKey());
+
+        try {
+            userService.read(userTO.getKey());
+            fail();
+        } catch (Exception ignore) {
+            assertNotNull(ignore);
+        }
+
+        try {
+            userWorkflowService.getFormsByName(userTO.getKey(), "Create approval");
+            fail();
+        } catch (Exception ignore) {
+            assertNotNull(ignore);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/17d5d892/fit/core-reference/src/test/java/org/apache/syncope/fit/core/VirAttrITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/VirAttrITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/VirAttrITCase.java
new file mode 100644
index 0000000..0951eee
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/VirAttrITCase.java
@@ -0,0 +1,678 @@
+/*
+ * 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;
+
+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.Locale;
+import java.util.Map;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Response;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.syncope.common.lib.patch.PasswordPatch;
+import org.apache.syncope.common.lib.patch.StatusPatch;
+import org.apache.syncope.common.lib.patch.StringPatchItem;
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.AnyTypeClassTO;
+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.ProvisioningResult;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.to.VirSchemaTO;
+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.PatchOperation;
+import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.syncope.common.lib.types.StatusPatchType;
+import org.apache.syncope.common.rest.api.service.AnyTypeClassService;
+import org.apache.syncope.common.rest.api.service.ResourceService;
+import org.apache.syncope.fit.AbstractITCase;
+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");
+        userTO.getVirAttrs().add(attrTO("virtualdata", "virtualvalue"));
+        userTO.getResources().add(RESOURCE_NAME_DBVIRATTR);
+        userTO.getMemberships().add(new MembershipTO.Builder().group(8L).build());
+
+        // 1. create user
+        userTO = createUser(userTO).getAny();
+        assertNotNull(userTO);
+
+        // 2. check for virtual attribute value
+        userTO = userService.read(userTO.getKey());
+        assertNotNull(userTO);
+        assertEquals("virtualvalue", userTO.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+        UserPatch userPatch = new UserPatch();
+        userPatch.setKey(userTO.getKey());
+        userPatch.getVirAttrs().add(attrTO("virtualdata", "virtualupdated"));
+
+        // 3. update virtual attribute
+        userTO = updateUser(userPatch).getAny();
+        assertNotNull(userTO);
+
+        // 4. check for virtual attribute value
+        userTO = userService.read(userTO.getKey());
+        assertNotNull(userTO);
+        assertEquals("virtualupdated", userTO.getVirAttrMap().get("virtualdata").getValues().get(0));
+    }
+
+    @Test
+    public void issueSYNCOPE260() {
+        // create new virtual schema for the resource below
+        ResourceTO ws2 = resourceService.read(RESOURCE_NAME_WS2);
+        ProvisionTO provision = ws2.getProvision(AnyTypeKind.USER.name());
+        assertNotNull(provision);
+
+        VirSchemaTO virSchema = new VirSchemaTO();
+        virSchema.setKey("syncope260" + getUUIDString());
+        virSchema.setExtAttrName("companyName");
+        virSchema.setProvision(provision.getKey());
+        virSchema = createSchema(SchemaType.VIRTUAL, virSchema);
+        assertNotNull(virSchema);
+
+        AnyTypeClassTO newClass = new AnyTypeClassTO();
+        newClass.setKey("syncope260" + getUUIDString());
+        newClass.getVirSchemas().add(virSchema.getKey());
+        Response response = anyTypeClassService.create(newClass);
+        assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatusInfo().getStatusCode());
+        newClass = getObject(response.getLocation(), AnyTypeClassService.class, AnyTypeClassTO.class);
+
+        // ----------------------------------
+        // create user and check virtual attribute value propagation
+        // ----------------------------------
+        UserTO userTO = UserITCase.getUniqueSampleTO("260@a.com");
+        userTO.getAuxClasses().add(newClass.getKey());
+        userTO.getVirAttrs().add(attrTO(virSchema.getKey(), "virtualvalue"));
+        userTO.getResources().add(RESOURCE_NAME_WS2);
+
+        ProvisioningResult<UserTO> result = createUser(userTO);
+        assertNotNull(result);
+        assertFalse(result.getPropagationStatuses().isEmpty());
+        assertEquals(RESOURCE_NAME_WS2, result.getPropagationStatuses().get(0).getResource());
+        assertEquals(PropagationTaskExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
+        userTO = result.getAny();
+
+        ConnObjectTO connObjectTO =
+                resourceService.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
+        assertEquals("virtualvalue", connObjectTO.getPlainAttrMap().get("COMPANYNAME").getValues().get(0));
+        // ----------------------------------
+
+        // ----------------------------------
+        // update user virtual attribute and check virtual attribute value update propagation
+        // ----------------------------------
+        UserPatch userPatch = new UserPatch();
+        userPatch.setKey(userTO.getKey());
+        userPatch.getVirAttrs().add(attrTO(virSchema.getKey(), "virtualvalue2"));
+
+        result = updateUser(userPatch);
+        assertNotNull(result);
+        assertFalse(result.getPropagationStatuses().isEmpty());
+        assertEquals(RESOURCE_NAME_WS2, result.getPropagationStatuses().get(0).getResource());
+        assertEquals(PropagationTaskExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
+        userTO = result.getAny();
+
+        connObjectTO = resourceService.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
+        assertEquals("virtualvalue2", connObjectTO.getPlainAttrMap().get("COMPANYNAME").getValues().get(0));
+        // ----------------------------------
+
+        // ----------------------------------
+        // suspend/reactivate user and check virtual attribute value (unchanged)
+        // ----------------------------------
+        StatusPatch statusPatch = new StatusPatch();
+        statusPatch.setKey(userTO.getKey());
+        statusPatch.setType(StatusPatchType.SUSPEND);
+        userTO = userService.status(statusPatch).readEntity(new GenericType<ProvisioningResult<UserTO>>() {
+        }).getAny();
+        assertEquals("suspended", userTO.getStatus());
+
+        connObjectTO = resourceService.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
+        assertEquals("virtualvalue2", connObjectTO.getPlainAttrMap().get("COMPANYNAME").getValues().get(0));
+
+        statusPatch = new StatusPatch();
+        statusPatch.setKey(userTO.getKey());
+        statusPatch.setType(StatusPatchType.REACTIVATE);
+        userTO = userService.status(statusPatch).readEntity(new GenericType<ProvisioningResult<UserTO>>() {
+        }).getAny();
+        assertEquals("active", userTO.getStatus());
+
+        connObjectTO = resourceService.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
+        assertEquals("virtualvalue2", connObjectTO.getPlainAttrMap().get("COMPANYNAME").getValues().get(0));
+        // ----------------------------------
+
+        // ----------------------------------
+        // update user attribute and check virtual attribute value (unchanged)
+        // ----------------------------------
+        userPatch = new UserPatch();
+        userPatch.setKey(userTO.getKey());
+        userPatch.getPlainAttrs().add(attrAddReplacePatch("surname", "Surname2"));
+
+        result = updateUser(userPatch);
+        assertNotNull(result);
+        assertFalse(result.getPropagationStatuses().isEmpty());
+        assertEquals(RESOURCE_NAME_WS2, result.getPropagationStatuses().get(0).getResource());
+        assertEquals(PropagationTaskExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
+        userTO = result.getAny();
+
+        connObjectTO = resourceService.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
+        assertEquals("Surname2", connObjectTO.getPlainAttrMap().get("SURNAME").getValues().get(0));
+
+        // virtual attribute value did not change
+        assertFalse(connObjectTO.getPlainAttrMap().get("COMPANYNAME").getValues().isEmpty());
+        assertEquals("virtualvalue2", connObjectTO.getPlainAttrMap().get("COMPANYNAME").getValues().get(0));
+        // ----------------------------------
+    }
+
+    @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).getAny();
+        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));
+
+        UserPatch userPatch = new UserPatch();
+        userPatch.setKey(actual.getKey());
+        userPatch.getVirAttrs().add(attrTO("virtualdata", "virtualupdated"));
+
+        // 5. update virtual attribute
+        actual = updateUser(userPatch).getAny();
+        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);
+
+        // change mapping of resource-csv
+        MappingTO origMapping = SerializationUtils.clone(csv.getProvisions().get(0).getMapping());
+        try {
+            // remove this mapping
+            CollectionUtils.filterInverse(csv.getProvisions().get(0).getMapping().getItems(),
+                    new Predicate<MappingItemTO>() {
+
+                        @Override
+                        public boolean evaluate(final MappingItemTO item) {
+                            return "email".equals(item.getIntAttrName());
+                        }
+                    });
+
+            resourceService.update(csv);
+            csv = resourceService.read(RESOURCE_NAME_CSV);
+            assertNotNull(csv.getProvisions().get(0).getMapping());
+
+            // create new virtual schema for the resource below
+            ProvisionTO provision = csv.getProvision(AnyTypeKind.USER.name());
+            assertNotNull(provision);
+
+            VirSchemaTO virSchema = new VirSchemaTO();
+            virSchema.setKey("syncope397" + getUUIDString());
+            virSchema.setExtAttrName("email");
+            virSchema.setProvision(provision.getKey());
+            virSchema = createSchema(SchemaType.VIRTUAL, virSchema);
+            assertNotNull(virSchema);
+
+            AnyTypeClassTO newClass = new AnyTypeClassTO();
+            newClass.setKey("syncope397" + getUUIDString());
+            newClass.getVirSchemas().add(virSchema.getKey());
+            Response response = anyTypeClassService.create(newClass);
+            assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatusInfo().getStatusCode());
+            newClass = getObject(response.getLocation(), AnyTypeClassService.class, AnyTypeClassTO.class);
+
+            // create a new user
+            UserTO userTO = UserITCase.getUniqueSampleTO("397@syncope.apache.org");
+            userTO.getAuxClasses().add("csv");
+            userTO.getAuxClasses().add(newClass.getKey());
+            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(virSchema.getKey(), "test@testone.org"));
+            // assign resource-csv to user
+            userTO.getResources().add(RESOURCE_NAME_CSV);
+            // save user
+            userTO = createUser(userTO).getAny();
+            // make std controls about user
+            assertNotNull(userTO);
+            assertTrue(RESOURCE_NAME_CSV.equals(userTO.getResources().iterator().next()));
+            assertEquals("test@testone.org", userTO.getVirAttrs().iterator().next().getValues().get(0));
+
+            // update user
+            UserTO toBeUpdated = userService.read(userTO.getKey());
+            UserPatch userPatch = new UserPatch();
+            userPatch.setKey(toBeUpdated.getKey());
+            userPatch.setPassword(new PasswordPatch.Builder().value("password234").build());
+            // assign new resource to user
+            userPatch.getResources().add(new StringPatchItem.Builder().
+                    operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_WS2).build());
+            // modify virtual attribute
+            userPatch.getVirAttrs().add(attrTO(virSchema.getKey(), "test@testoneone.com"));
+
+            // check Syncope change password
+            userPatch.setPassword(new PasswordPatch.Builder().
+                    value("password234").
+                    onSyncope(true).
+                    resource(RESOURCE_NAME_WS2).
+                    build());
+
+            ProvisioningResult<UserTO> result = updateUser(userPatch);
+            assertNotNull(result);
+            toBeUpdated = result.getAny();
+            assertTrue(toBeUpdated.getVirAttrs().iterator().next().getValues().contains("test@testoneone.com"));
+            // check if propagates correctly with assertEquals on size of tasks list
+            assertEquals(2, result.getPropagationStatuses().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 = createUser(userTO).getAny();
+        assertNotNull(userTO);
+
+        // 2. check for virtual attribute value
+        userTO = userService.read(userTO.getKey());
+        assertEquals("virattrcache", userTO.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+        // ----------------------------------------
+        // 3. change connector URL so that we are sure that any provided value will come from virtual cache
+        // ----------------------------------------
+        String jdbcURL = null;
+        ConnInstanceTO connInstanceTO = connectorService.readByResource(
+                RESOURCE_NAME_DBVIRATTR, Locale.ENGLISH.getLanguage());
+        for (ConnConfProperty prop : connInstanceTO.getConf()) {
+            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);
+        // ----------------------------------------
+
+        // ----------------------------------------
+        // 4. update value on external resource
+        // ----------------------------------------
+        JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+
+        String value = jdbcTemplate.queryForObject(
+                "SELECT USERNAME FROM testsync WHERE ID=?", String.class, userTO.getKey());
+        assertEquals("virattrcache", value);
+
+        jdbcTemplate.update("UPDATE testsync set USERNAME='virattrcache2' WHERE ID=?", userTO.getKey());
+
+        value = jdbcTemplate.queryForObject(
+                "SELECT USERNAME FROM testsync WHERE ID=?", String.class, userTO.getKey());
+        assertEquals("virattrcache2", value);
+        // ----------------------------------------
+
+        userTO = userService.read(userTO.getKey());
+        assertEquals("virattrcache", userTO.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+        // ----------------------------------------
+        // 5. restore connector URL, values can be read again from external resource
+        // ----------------------------------------
+        for (ConnConfProperty prop : connInstanceTO.getConf()) {
+            if ("jdbcUrlTemplate".equals(prop.getSchema().getName())) {
+                prop.getValues().clear();
+                prop.getValues().add(jdbcURL);
+            }
+        }
+
+        connectorService.update(connInstanceTO);
+        // ----------------------------------------
+
+        // cached value still in place...
+        userTO = userService.read(userTO.getKey());
+        assertEquals("virattrcache", userTO.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+        // force cache update by adding a resource which has virtualdata mapped for propagation
+        UserPatch userPatch = new UserPatch();
+        userPatch.setKey(userTO.getKey());
+        userPatch.getResources().add(new StringPatchItem.Builder().
+                operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_WS2).build());
+        userTO = updateUser(userPatch).getAny();
+        assertNotNull(userTO);
+
+        userTO = userService.read(userTO.getKey());
+        assertEquals("virattrcache2", userTO.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).getAny();
+        // 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).getAny();
+        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();
+
+        userTO.getMemberships().add(new MembershipTO.Builder().group(groupTO.getKey()).build());
+
+        ProvisioningResult<UserTO> result = createUser(userTO);
+        assertEquals(2, result.getPropagationStatuses().size());
+        assertEquals(PropagationTaskExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
+        assertEquals(PropagationTaskExecStatus.SUCCESS, result.getPropagationStatuses().get(1).getStatus());
+        userTO = result.getAny();
+
+        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.getResources().add(RESOURCE_NAME_LDAP);
+        userTO.getMemberships().clear();
+        userTO.getVirAttrs().clear();
+
+        userTO = createUser(userTO).getAny();
+
+        assertNotNull(userTO.getVirAttrMap().get("virtualReadOnly"));
+    }
+
+    @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
+        userTO.getVirAttrs().add(attrTO("virtualdata", "syncope501@apache.org"));
+
+        userTO = createUser(userTO).getAny();
+
+        assertNotNull(userTO.getVirAttrMap().get("virtualdata"));
+        assertEquals("syncope501@apache.org", userTO.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+        // 2. update virtual attribute
+        UserPatch userPatch = new UserPatch();
+        userPatch.setKey(userTO.getKey());
+        // change virtual attribute value
+        userPatch.getVirAttrs().add(attrTO("virtualdata", "syncope501_updated@apache.org"));
+
+        userTO = updateUser(userPatch).getAny();
+        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() {
+        ResourceTO ldap = resourceService.read(RESOURCE_NAME_LDAP);
+        try {
+            ProvisionTO provision = ldap.getProvision(AnyTypeKind.USER.name());
+            assertNotNull(provision);
+            CollectionUtils.filterInverse(provision.getMapping().getItems(), new Predicate<MappingItemTO>() {
+
+                @Override
+                public boolean evaluate(final MappingItemTO item) {
+                    return "mail".equals(item.getExtAttrName());
+                }
+            });
+            provision.getVirSchemas().clear();
+
+            ldap.setKey(RESOURCE_NAME_LDAP + "691" + getUUIDString());
+            resourceService.create(ldap);
+
+            ldap = resourceService.read(ldap.getKey());
+            provision = ldap.getProvision(AnyTypeKind.USER.name());
+            assertNotNull(provision);
+
+            // create new virtual schema for the resource below
+            VirSchemaTO virSchema = new VirSchemaTO();
+            virSchema.setKey("syncope691" + getUUIDString());
+            virSchema.setExtAttrName("mail");
+            virSchema.setProvision(provision.getKey());
+            virSchema = createSchema(SchemaType.VIRTUAL, virSchema);
+            assertNotNull(virSchema);
+
+            AnyTypeClassTO newClass = new AnyTypeClassTO();
+            newClass.setKey("syncope691" + getUUIDString());
+            newClass.getVirSchemas().add(virSchema.getKey());
+            Response response = anyTypeClassService.create(newClass);
+            assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatusInfo().getStatusCode());
+            newClass = getObject(response.getLocation(), AnyTypeClassService.class, AnyTypeClassTO.class);
+
+            // create a new user
+            UserTO userTO = UserITCase.getUniqueSampleTO("syncope691@syncope.apache.org");
+            userTO.getAuxClasses().add(newClass.getKey());
+            userTO.getResources().clear();
+            userTO.getMemberships().clear();
+            userTO.getDerAttrs().clear();
+            userTO.getVirAttrs().clear();
+
+            AttrTO emailTO = new AttrTO();
+            emailTO.setSchema(virSchema.getKey());
+            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(ldap.getKey());
+            // save user
+            userTO = createUser(userTO).getAny();
+            // make std controls about user
+            assertNotNull(userTO);
+            assertTrue(ldap.getKey().equals(userTO.getResources().iterator().next()));
+
+            assertEquals(2, userTO.getVirAttrs().iterator().next().getValues().size(), 0);
+            assertTrue(userTO.getVirAttrs().iterator().next().getValues().contains("test@issue691.dom1.org"));
+            assertTrue(userTO.getVirAttrs().iterator().next().getValues().contains("test@issue691.dom2.org"));
+
+            // update user
+            UserPatch userPatch = new UserPatch();
+            userPatch.setKey(userTO.getKey());
+            // modify virtual attribute
+            userPatch.getVirAttrs().add(
+                    new AttrTO.Builder().schema(virSchema.getKey()).
+                    value("test@issue691.dom3.org").
+                    value("test@issue691.dom4.org").
+                    build());
+
+            UserTO updated = updateUser(userPatch).getAny();
+            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(ldap.getKey());
+            } catch (Exception ignore) {
+                // ignore
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/17d5d892/fit/core-reference/src/test/java/org/apache/syncope/fit/core/VirSchemaITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/VirSchemaITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/VirSchemaITCase.java
new file mode 100644
index 0000000..01ce60f
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/VirSchemaITCase.java
@@ -0,0 +1,152 @@
+/*
+ * 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;
+
+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 static org.junit.Assert.fail;
+
+import java.security.AccessControlException;
+import java.util.List;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.to.VirSchemaTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.EntityViolationType;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.syncope.common.rest.api.beans.SchemaQuery;
+import org.apache.syncope.common.rest.api.service.SchemaService;
+import org.apache.syncope.fit.AbstractITCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class VirSchemaITCase extends AbstractITCase {
+
+    @Test
+    public void list() {
+        List<VirSchemaTO> vSchemas = schemaService.list(new SchemaQuery.Builder().type(SchemaType.VIRTUAL).build());
+        assertFalse(vSchemas.isEmpty());
+        for (VirSchemaTO vSchemaTO : vSchemas) {
+            assertNotNull(vSchemaTO);
+        }
+    }
+
+    @Test
+    public void crud() {
+        ResourceTO csv = resourceService.read(RESOURCE_NAME_CSV);
+        assertNotNull(csv);
+        assertEquals(1, csv.getProvisions().size());
+        assertTrue(csv.getProvisions().get(0).getVirSchemas().isEmpty());
+
+        VirSchemaTO schema = new VirSchemaTO();
+        schema.setKey("virtualTest" + getUUIDString());
+        schema.setExtAttrName("name");
+        schema.setProvision(csv.getProvisions().get(0).getKey());
+
+        schema = createSchema(SchemaType.VIRTUAL, schema);
+        assertNotNull(schema);
+        assertEquals(csv.getProvisions().get(0).getKey(), schema.getProvision(), 0);
+
+        csv = resourceService.read(RESOURCE_NAME_CSV);
+        assertNotNull(csv);
+        assertEquals(1, csv.getProvisions().size());
+        assertFalse(csv.getProvisions().get(0).getVirSchemas().isEmpty());
+
+        schema = schemaService.read(SchemaType.VIRTUAL, schema.getKey());
+        assertNotNull(schema);
+
+        schemaService.delete(SchemaType.VIRTUAL, schema.getKey());
+
+        try {
+            schemaService.read(SchemaType.VIRTUAL, schema.getKey());
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+        }
+
+        csv = resourceService.read(RESOURCE_NAME_CSV);
+        assertNotNull(csv);
+        assertEquals(1, csv.getProvisions().size());
+        assertTrue(csv.getProvisions().get(0).getVirSchemas().isEmpty());
+    }
+
+    @Test
+    public void anonymous() {
+        SchemaService unauthenticated = clientFactory.create().getService(SchemaService.class);
+        try {
+            unauthenticated.list(new SchemaQuery.Builder().type(SchemaType.VIRTUAL).build());
+            fail();
+        } catch (AccessControlException e) {
+            assertNotNull(e);
+        }
+
+        SchemaService anonymous = clientFactory.create(ANONYMOUS_UNAME, ANONYMOUS_KEY).getService(SchemaService.class);
+        assertFalse(anonymous.list(new SchemaQuery.Builder().type(SchemaType.VIRTUAL).build()).isEmpty());
+    }
+
+    @Test
+    public void issueSYNCOPE323() {
+        VirSchemaTO actual = schemaService.read(SchemaType.VIRTUAL, "virtualdata");
+        assertNotNull(actual);
+
+        try {
+            createSchema(SchemaType.VIRTUAL, actual);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.CONFLICT, e.getType().getResponseStatus());
+            assertEquals(ClientExceptionType.EntityExists, e.getType());
+        }
+
+        actual.setKey(null);
+        try {
+            createSchema(SchemaType.VIRTUAL, actual);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.BAD_REQUEST, e.getType().getResponseStatus());
+            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE418() {
+        ResourceTO ws1 = resourceService.read(RESOURCE_NAME_WS1);
+        assertNotNull(ws1);
+        assertEquals(1, ws1.getProvisions().size());
+        assertTrue(ws1.getProvisions().get(0).getVirSchemas().isEmpty());
+
+        VirSchemaTO schema = new VirSchemaTO();
+        schema.setKey("http://schemas.examples.org/security/authorization/organizationUnit");
+        schema.setExtAttrName("name");
+        schema.setProvision(ws1.getProvisions().get(0).getKey());
+
+        try {
+            createSchema(SchemaType.VIRTUAL, schema);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidVirSchema, e.getType());
+
+            assertTrue(e.getElements().iterator().next().contains(EntityViolationType.InvalidName.name()));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/17d5d892/fit/core-reference/src/test/java/org/apache/syncope/fit/core/WorkflowITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/WorkflowITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/WorkflowITCase.java
new file mode 100644
index 0000000..d111782
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/WorkflowITCase.java
@@ -0,0 +1,78 @@
+/*
+ * 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;
+
+import org.apache.syncope.fit.ActivitiDetector;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import java.io.IOException;
+import java.io.InputStream;
+import javax.ws.rs.core.Response;
+import org.apache.commons.io.IOUtils;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.fit.AbstractITCase;
+import org.junit.Assume;
+import org.junit.Test;
+
+public class WorkflowITCase extends AbstractITCase {
+
+    private void exportDefinition(final AnyTypeKind type) throws IOException {
+        Response response = workflowService.exportDefinition(type);
+        assertTrue(response.getMediaType().toString().
+                startsWith(clientFactory.getContentType().getMediaType().toString()));
+        assertTrue(response.getEntity() instanceof InputStream);
+        String definition = IOUtils.toString((InputStream) response.getEntity());
+        assertNotNull(definition);
+        assertFalse(definition.isEmpty());
+    }
+
+    @Test
+    public void exportUserDefinition() throws IOException {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(syncopeService));
+        exportDefinition(AnyTypeKind.USER);
+    }
+
+    @Test
+    public void getGroupDefinition() throws IOException {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForGroups(syncopeService));
+        exportDefinition(AnyTypeKind.GROUP);
+    }
+
+    private void importDefinition(final AnyTypeKind type) throws IOException {
+        Response response = workflowService.exportDefinition(type);
+        String definition = IOUtils.toString((InputStream) response.getEntity());
+
+        workflowService.importDefinition(type, definition);
+    }
+
+    @Test
+    public void updateUserDefinition() throws IOException {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(syncopeService));
+        importDefinition(AnyTypeKind.USER);
+    }
+
+    @Test
+    public void updateGroupDefinition() throws IOException {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForGroups(syncopeService));
+        importDefinition(AnyTypeKind.GROUP);
+    }
+}