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 2015/01/23 17:41:23 UTC

[01/15] syncope git commit: [SYNCOPE-629] Format + safely delete role

Repository: syncope
Updated Branches:
  refs/heads/2_0_X 8e45a8e67 -> 4e1ae7ac9


[SYNCOPE-629] Format + safely delete role


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/4efca7c3
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/4efca7c3
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/4efca7c3

Branch: refs/heads/2_0_X
Commit: 4efca7c3ec3973ab4b774a1f43e4403daf3354b7
Parents: f2d45fd
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Wed Jan 21 06:54:34 2015 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Wed Jan 21 06:54:34 2015 +0100

----------------------------------------------------------------------
 .../core/rest/ConfigurationTestITCase.java      | 79 +++++++++++---------
 1 file changed, 42 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/4efca7c3/core/src/test/java/org/apache/syncope/core/rest/ConfigurationTestITCase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/syncope/core/rest/ConfigurationTestITCase.java b/core/src/test/java/org/apache/syncope/core/rest/ConfigurationTestITCase.java
index cbe13d5..1f981ac 100644
--- a/core/src/test/java/org/apache/syncope/core/rest/ConfigurationTestITCase.java
+++ b/core/src/test/java/org/apache/syncope/core/rest/ConfigurationTestITCase.java
@@ -151,22 +151,22 @@ public class ConfigurationTestITCase extends AbstractTest {
             assertTrue(e.getElements().iterator().next().contains(EntityViolationType.InvalidName.name()));
         }
     }
-    
+
     @Test
-    public void issueSYNCOPE629() throws IOException{
+    public void issueSYNCOPE629() throws IOException {
         SchemaTO membershipKey = new SchemaTO();
-        membershipKey.setName("membershipKey"+getUUIDString());
+        membershipKey.setName("membershipKey" + getUUIDString());
         membershipKey.setType(AttributeSchemaType.String);
         createSchema(AttributableType.MEMBERSHIP, SchemaType.NORMAL, membershipKey);
-        
+
         SchemaTO roleKey = new SchemaTO();
-        roleKey.setName("roleKey"+getUUIDString());
+        roleKey.setName("roleKey" + getUUIDString());
         roleKey.setType(AttributeSchemaType.String);
-        createSchema(AttributableType.ROLE, SchemaType.NORMAL, roleKey);        
-                
+        createSchema(AttributableType.ROLE, SchemaType.NORMAL, roleKey);
+
         RoleTO roleTO = new RoleTO();
         roleTO.setName("aRole" + getUUIDString());
-        roleTO.setParent(8L);        
+        roleTO.setParent(8L);
         // verify inheritance password and account policies
         roleTO.setInheritAccountPolicy(false);
         // not inherited so setter execution shouldn't be ignored
@@ -176,39 +176,44 @@ public class ConfigurationTestITCase extends AbstractTest {
         roleTO.setPasswordPolicy(2L);
         roleTO.getRAttrTemplates().add("icon");
         roleTO.getAttrs().add(attributeTO("icon", "anIcon"));
-        roleTO.getResources().add(RESOURCE_NAME_LDAP);       
+        roleTO.getResources().add(RESOURCE_NAME_LDAP);
         roleTO.getMAttrTemplates().add(membershipKey.getName());
         roleTO.getRAttrTemplates().add(roleKey.getName());
         RoleTO testRole = createRole(roleTO);
-                       
-        Response response = configurationService.export();
-        assertNotNull(response);
-        assertEquals(Response.Status.OK.getStatusCode(), response.getStatusInfo().getStatusCode());
-        assertTrue(response.getMediaType().toString().startsWith(MediaType.TEXT_XML));
-        String contentDisposition = response.getHeaderString(HttpHeaders.CONTENT_DISPOSITION);
-        assertNotNull(contentDisposition);
 
-        Object entity = response.getEntity();
-        assertTrue(entity instanceof InputStream);
-        String configExport = IOUtils.toString((InputStream) entity, SyncopeConstants.DEFAULT_ENCODING);
-        assertFalse(configExport.isEmpty());
-        assertTrue(configExport.length() > 1000);
-        
-        String[] result = StringUtils.substringsBetween(configExport, "<RATTRTEMPLATE", "/>");
-        boolean rattrExists = false;
-        for(String entry : result){
-            if(entry.contains(roleKey.getName())) rattrExists = true;
-        }
-        assertTrue(rattrExists);
-        
-        result = StringUtils.substringsBetween(configExport, "<MATTRTEMPLATE", "/>");
-        boolean mattrExists = false;
-        for(String entry : result){
-            if(entry.contains(membershipKey.getName())) mattrExists = true;
+        try {
+            Response response = configurationService.export();
+            assertNotNull(response);
+            assertEquals(Response.Status.OK.getStatusCode(), response.getStatusInfo().getStatusCode());
+            assertTrue(response.getMediaType().toString().startsWith(MediaType.TEXT_XML));
+            String contentDisposition = response.getHeaderString(HttpHeaders.CONTENT_DISPOSITION);
+            assertNotNull(contentDisposition);
+
+            Object entity = response.getEntity();
+            assertTrue(entity instanceof InputStream);
+            String configExport = IOUtils.toString((InputStream) entity, SyncopeConstants.DEFAULT_ENCODING);
+            assertFalse(configExport.isEmpty());
+            assertTrue(configExport.length() > 1000);
+
+            String[] result = StringUtils.substringsBetween(configExport, "<RATTRTEMPLATE", "/>");
+            boolean rattrExists = false;
+            for (String entry : result) {
+                if (entry.contains(roleKey.getName())) {
+                    rattrExists = true;
+                }
+            }
+            assertTrue(rattrExists);
+
+            result = StringUtils.substringsBetween(configExport, "<MATTRTEMPLATE", "/>");
+            boolean mattrExists = false;
+            for (String entry : result) {
+                if (entry.contains(membershipKey.getName())) {
+                    mattrExists = true;
+                }
+            }
+            assertTrue(mattrExists);
+        } finally {
+            deleteRole(testRole.getId());
         }
-        assertTrue(mattrExists);
-        
-        deleteRole(testRole.getId());
-
     }
 }


[13/15] syncope git commit: FIT server integration tests

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/AuthenticationITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/AuthenticationITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/AuthenticationITCase.java
new file mode 100644
index 0000000..9c18a4d
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/AuthenticationITCase.java
@@ -0,0 +1,440 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.security.AccessControlException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.core.Response;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.mod.StatusMod;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.PlainSchemaTO;
+import org.apache.syncope.common.lib.to.RoleTO;
+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.AttrSchemaType;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.CipherAlgorithm;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.syncope.common.lib.wrap.EntitlementTO;
+import org.apache.syncope.common.lib.wrap.ResourceName;
+import org.apache.syncope.common.rest.api.CollectionWrapper;
+import org.apache.syncope.common.rest.api.service.EntitlementService;
+import org.apache.syncope.common.rest.api.service.SchemaService;
+import org.apache.syncope.common.rest.api.service.UserSelfService;
+import org.apache.syncope.common.rest.api.service.UserService;
+import org.apache.syncope.server.misc.security.Encryptor;
+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 AuthenticationITCase extends AbstractITCase {
+
+    private int getFailedLogins(UserService testUserService, long userId) {
+        UserTO readUserTO = testUserService.read(userId);
+        assertNotNull(readUserTO);
+        assertNotNull(readUserTO.getFailedLogins());
+        return readUserTO.getFailedLogins();
+    }
+
+    private void assertReadFails(UserService userService, long id) {
+        try {
+            userService.read(id);
+            fail("access should not work");
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+    }
+
+    @Test
+    public void testAdminEntitlements() {
+        // 1. as anonymous, read all available entitlements
+        List<EntitlementTO> allEntitlements = entitlementService.getAllEntitlements();
+        assertNotNull(allEntitlements);
+        assertFalse(allEntitlements.isEmpty());
+
+        // 2. as admin, read own entitlements
+        List<EntitlementTO> adminEntitlements = entitlementService.getOwnEntitlements();
+
+        assertEquals(new HashSet<String>(CollectionWrapper.unwrap(allEntitlements)),
+                new HashSet<String>(CollectionWrapper.unwrap(adminEntitlements)));
+    }
+
+    @Test
+    public void testUserSchemaAuthorization() {
+        // 0. create a role that can only read schemas
+        RoleTO authRoleTO = new RoleTO();
+        authRoleTO.setName("authRole" + getUUIDString());
+        authRoleTO.setParent(8L);
+        authRoleTO.getEntitlements().add("SCHEMA_READ");
+
+        authRoleTO = createRole(authRoleTO);
+        assertNotNull(authRoleTO);
+
+        String schemaName = "authTestSchema" + getUUIDString();
+
+        // 1. create a schema (as admin)
+        PlainSchemaTO schemaTO = new PlainSchemaTO();
+        schemaTO.setKey(schemaName);
+        schemaTO.setMandatoryCondition("false");
+        schemaTO.setType(AttrSchemaType.String);
+
+        PlainSchemaTO newPlainSchemaTO = createSchema(AttributableType.USER, SchemaType.PLAIN, schemaTO);
+        assertEquals(schemaTO, newPlainSchemaTO);
+
+        // 2. create an user with the role created above (as admin)
+        UserTO userTO = UserITCase.getUniqueSampleTO("auth@test.org");
+
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(authRoleTO.getKey());
+        AttrTO testAttrTO = new AttrTO();
+        testAttrTO.setSchema("testAttribute");
+        testAttrTO.getValues().add("a value");
+        membershipTO.getPlainAttrs().add(testAttrTO);
+        userTO.getMemberships().add(membershipTO);
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        // 3. read the schema created above (as admin) - success
+        schemaTO = schemaService.read(AttributableType.USER, SchemaType.PLAIN, schemaName);
+        assertNotNull(schemaTO);
+
+        // 4. read the schema created above (as user) - success
+        SchemaService schemaService2 = clientFactory.create(userTO.getUsername(), "password123").getService(
+                SchemaService.class);
+
+        schemaTO = schemaService2.read(AttributableType.USER, SchemaType.PLAIN, schemaName);
+        assertNotNull(schemaTO);
+
+        // 5. update the schema create above (as user) - failure
+        try {
+            schemaService2.update(AttributableType.ROLE, SchemaType.PLAIN, schemaName, schemaTO);
+            fail("Schemaupdate as user schould not work");
+        } catch (SyncopeClientException e) {
+            assertNotNull(e);
+            assertEquals(Response.Status.UNAUTHORIZED, e.getType().getResponseStatus());
+        } catch (AccessControlException e) {
+            // CXF Service will throw this exception
+            assertNotNull(e);
+        }
+
+        assertEquals(0, getFailedLogins(userService, userTO.getKey()));
+    }
+
+    @Test
+    public void testUserRead() {
+        UserTO userTO = UserITCase.getUniqueSampleTO("testuserread@test.org");
+
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(7L);
+        AttrTO testAttrTO = new AttrTO();
+        testAttrTO.setSchema("testAttribute");
+        testAttrTO.getValues().add("a value");
+        membershipTO.getPlainAttrs().add(testAttrTO);
+        userTO.getMemberships().add(membershipTO);
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        UserService userService2 = clientFactory.create(userTO.getUsername(), "password123").
+                getService(UserService.class);
+
+        UserTO readUserTO = userService2.read(1L);
+        assertNotNull(readUserTO);
+
+        UserService userService3 = clientFactory.create("verdi", ADMIN_PWD).getService(UserService.class);
+
+        SyncopeClientException exception = null;
+        try {
+            userService3.read(1L);
+            fail();
+        } catch (SyncopeClientException e) {
+            exception = e;
+        }
+        assertNotNull(exception);
+        assertEquals(ClientExceptionType.UnauthorizedRole, exception.getType());
+    }
+
+    @Test
+    public void testUserSearch() {
+        UserTO userTO = UserITCase.getUniqueSampleTO("testusersearch@test.org");
+
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(7L);
+        AttrTO testAttrTO = new AttrTO();
+        testAttrTO.setSchema("testAttribute");
+        testAttrTO.getValues().add("a value");
+        membershipTO.getPlainAttrs().add(testAttrTO);
+        userTO.getMemberships().add(membershipTO);
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        UserService userService2 = clientFactory.create(userTO.getUsername(), "password123").
+                getService(UserService.class);
+
+        PagedResult<UserTO> matchedUsers = userService2.search(
+                SyncopeClient.getUserSearchConditionBuilder().isNotNull("loginDate").query());
+        assertNotNull(matchedUsers);
+        assertFalse(matchedUsers.getResult().isEmpty());
+        Set<Long> userIds = new HashSet<Long>(matchedUsers.getResult().size());
+        for (UserTO user : matchedUsers.getResult()) {
+            userIds.add(user.getKey());
+        }
+        assertTrue(userIds.contains(1L));
+
+        UserService userService3 = clientFactory.create("verdi", "password").getService(UserService.class);
+
+        matchedUsers = userService3.search(
+                SyncopeClient.getUserSearchConditionBuilder().isNotNull("loginDate").query());
+        assertNotNull(matchedUsers);
+
+        userIds = new HashSet<>(matchedUsers.getResult().size());
+
+        for (UserTO user : matchedUsers.getResult()) {
+            userIds.add(user.getKey());
+        }
+        assertFalse(userIds.contains(1L));
+    }
+
+    @Test
+    public void checkFailedLogins() {
+        UserTO userTO = UserITCase.getUniqueSampleTO("checkFailedLogin@syncope.apache.org");
+
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(7L);
+        AttrTO testAttrTO = new AttrTO();
+        testAttrTO.setSchema("testAttribute");
+        testAttrTO.getValues().add("a value");
+        membershipTO.getPlainAttrs().add(testAttrTO);
+        userTO.getMemberships().add(membershipTO);
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+        long userId = userTO.getKey();
+
+        UserService userService2 = clientFactory.create(userTO.getUsername(), "password123").getService(
+                UserService.class);
+        assertEquals(0, getFailedLogins(userService2, userId));
+
+        // authentications failed ...
+        UserService userService3 = clientFactory.create(userTO.getUsername(), "wrongpwd1").getService(
+                UserService.class);
+        assertReadFails(userService3, userId);
+        assertReadFails(userService3, userId);
+
+        assertEquals(2, getFailedLogins(userService, userId));
+
+        UserService userService4 = clientFactory.create(userTO.getUsername(), "password123").getService(
+                UserService.class);
+        assertEquals(0, getFailedLogins(userService4, userId));
+    }
+
+    @Test
+    public void checkUserSuspension() {
+        UserTO userTO = UserITCase.getUniqueSampleTO("checkSuspension@syncope.apache.org");
+
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(7L);
+        AttrTO testAttrTO = new AttrTO();
+        testAttrTO.setSchema("testAttribute");
+        testAttrTO.getValues().add("a value");
+        membershipTO.getPlainAttrs().add(testAttrTO);
+        userTO.getMemberships().add(membershipTO);
+
+        userTO = createUser(userTO);
+        long userId = userTO.getKey();
+        assertNotNull(userTO);
+
+        UserService userService2 = clientFactory.create(userTO.getUsername(), "password123").
+                getService(UserService.class);
+        assertEquals(0, getFailedLogins(userService2, userId));
+
+        // authentications failed ...
+        UserService userService3 = clientFactory.create(userTO.getUsername(), "wrongpwd1").
+                getService(UserService.class);
+        assertReadFails(userService3, userId);
+        assertReadFails(userService3, userId);
+        assertReadFails(userService3, userId);
+
+        assertEquals(3, getFailedLogins(userService, userId));
+
+        // last authentication before suspension
+        assertReadFails(userService3, userId);
+
+        userTO = userService.read(userTO.getKey());
+        assertNotNull(userTO);
+        assertNotNull(userTO.getFailedLogins());
+        assertEquals(3, userTO.getFailedLogins(), 0);
+        assertEquals("suspended", userTO.getStatus());
+
+        // Access with correct credentials should fail as user is suspended
+        userService2 = clientFactory.create(userTO.getUsername(), "password123").getService(UserService.class);
+        assertReadFails(userService2, userId);
+
+        StatusMod reactivate = new StatusMod();
+        reactivate.setType(StatusMod.ModType.REACTIVATE);
+        userTO = userService.status(userTO.getKey(), reactivate).readEntity(UserTO.class);
+        assertNotNull(userTO);
+        assertEquals("active", userTO.getStatus());
+
+        userService2 = clientFactory.create(userTO.getUsername(), "password123").getService(UserService.class);
+        assertEquals(0, getFailedLogins(userService2, userId));
+    }
+
+    @Test
+    public void issueSYNCOPE48() {
+        // Parent role, able to create users with role 1
+        RoleTO parentRole = new RoleTO();
+        parentRole.setName("parentAdminRole" + getUUIDString());
+        parentRole.getEntitlements().add("USER_CREATE");
+        parentRole.getEntitlements().add("ROLE_1");
+        parentRole.setParent(1L);
+        parentRole = createRole(parentRole);
+        assertNotNull(parentRole);
+
+        // Child role, with no entitlements
+        RoleTO childRole = new RoleTO();
+        childRole.setName("childAdminRole");
+        childRole.setParent(parentRole.getKey());
+
+        childRole = createRole(childRole);
+        assertNotNull(childRole);
+
+        // User with child role, created by admin
+        UserTO role1Admin = UserITCase.getUniqueSampleTO("syncope48admin@apache.org");
+        role1Admin.setPassword("password");
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(childRole.getKey());
+        role1Admin.getMemberships().add(membershipTO);
+
+        role1Admin = createUser(role1Admin);
+        assertNotNull(role1Admin);
+
+        UserService userService2 = clientFactory.create(role1Admin.getUsername(), "password").getService(
+                UserService.class);
+
+        // User with role 1, created by user with child role created above
+        UserTO role1User = UserITCase.getUniqueSampleTO("syncope48user@apache.org");
+        membershipTO = new MembershipTO();
+        membershipTO.setRoleId(1L);
+        role1User.getMemberships().add(membershipTO);
+
+        Response response = userService2.create(role1User, true);
+        assertNotNull(response);
+        role1User = response.readEntity(UserTO.class);
+        assertNotNull(role1User);
+    }
+
+    @Test
+    public void issueSYNCOPE434() {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers());
+
+        // 1. create user with role 9 (users with role 9 are defined in workflow as subject to approval)
+        UserTO userTO = UserITCase.getUniqueSampleTO("createWithReject@syncope.apache.org");
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(9L);
+        userTO.getMemberships().add(membershipTO);
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+        assertEquals("createApproval", userTO.getStatus());
+
+        // 2. try to authenticate: fail
+        EntitlementService myEntitlementService = clientFactory.create(userTO.getUsername(), "password123").
+                getService(EntitlementService.class);
+        try {
+            myEntitlementService.getOwnEntitlements();
+            fail();
+        } catch (AccessControlException e) {
+            assertNotNull(e);
+        }
+
+        // 3. approve user
+        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());
+
+        // 4. try to authenticate again: success
+        assertNotNull(myEntitlementService.getOwnEntitlements());
+    }
+
+    @Test
+    public void issueSYNCOPE164() throws Exception {
+        // 1. create user with db resource
+        UserTO user = UserITCase.getUniqueSampleTO("syncope164@syncope.apache.org");
+        user.setPassword("password1");
+        user.getResources().add(RESOURCE_NAME_TESTDB);
+        user = createUser(user);
+        assertNotNull(user);
+
+        // 2. unlink the resource from the created user
+        assertNotNull(userService.bulkDeassociation(user.getKey(),
+                ResourceDeassociationActionType.UNLINK,
+                CollectionWrapper.wrap(RESOURCE_NAME_TESTDB, ResourceName.class)).
+                readEntity(BulkActionResult.class));
+
+        // 3. change password on Syncope
+        UserMod userMod = new UserMod();
+        userMod.setKey(user.getKey());
+        userMod.setPassword("password2");
+        user = updateUser(userMod);
+        assertNotNull(user);
+
+        // 4. check that the db resource has still the initial password value
+        final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+        String value = jdbcTemplate.queryForObject(
+                "SELECT PASSWORD FROM test WHERE ID=?", String.class, user.getUsername());
+        assertEquals(Encryptor.getInstance().encode("password1", CipherAlgorithm.SHA1), value.toUpperCase());
+
+        // 5. successfully authenticate with old (on db resource) and new (on internal storage) password values
+        user = clientFactory.create(user.getUsername(), "password1").getService(UserSelfService.class).read();
+        assertNotNull(user);
+        user = clientFactory.create(user.getUsername(), "password2").getService(UserSelfService.class).read();
+        assertNotNull(user);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ConfigurationITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ConfigurationITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ConfigurationITCase.java
new file mode 100644
index 0000000..bf35a9c
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ConfigurationITCase.java
@@ -0,0 +1,210 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.ConfTO;
+import org.apache.syncope.common.lib.to.PlainSchemaTO;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.common.lib.types.AttributableType;
+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.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class ConfigurationITCase extends AbstractITCase {
+
+    @Test
+    public void create() {
+        PlainSchemaTO testKey = new PlainSchemaTO();
+        testKey.setKey("testKey");
+        testKey.setType(AttrSchemaType.String);
+        createSchema(AttributableType.CONFIGURATION, SchemaType.PLAIN, testKey);
+
+        AttrTO conf = new AttrTO();
+        conf.setSchema("testKey");
+        conf.getValues().add("testValue");
+
+        configurationService.set(conf.getSchema(), conf);
+
+        AttrTO actual = configurationService.read(conf.getSchema());
+        assertEquals(actual, conf);
+    }
+
+    @Test
+    public void delete() throws UnsupportedEncodingException {
+        try {
+            configurationService.delete("nonExistent");
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
+        }
+
+        AttrTO tokenLength = configurationService.read("token.length");
+
+        configurationService.delete("token.length");
+        try {
+            configurationService.read("token.length");
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
+        }
+
+        configurationService.set(tokenLength.getSchema(), tokenLength);
+
+        AttrTO actual = configurationService.read(tokenLength.getSchema());
+        assertEquals(actual, tokenLength);
+    }
+
+    @Test
+    public void list() {
+        ConfTO wholeConf = configurationService.list();
+        assertNotNull(wholeConf);
+        for (AttrTO conf : wholeConf.getPlainAttrs()) {
+            assertNotNull(conf);
+        }
+    }
+
+    @Test
+    public void read() {
+        AttrTO conf = configurationService.read("token.expireTime");
+        assertNotNull(conf);
+    }
+
+    @Test
+    public void update() {
+        AttrTO expireTime = configurationService.read("token.expireTime");
+        int value = Integer.parseInt(expireTime.getValues().get(0));
+        value++;
+        expireTime.getValues().set(0, value + "");
+
+        configurationService.set(expireTime.getSchema(), expireTime);
+
+        AttrTO newConfigurationTO = configurationService.read(expireTime.getSchema());
+        assertEquals(expireTime, newConfigurationTO);
+    }
+
+    @Test
+    public void dbExport() throws IOException {
+        Response response = configurationService.export();
+        assertNotNull(response);
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatusInfo().getStatusCode());
+        assertTrue(response.getMediaType().toString().startsWith(MediaType.TEXT_XML));
+        String contentDisposition = response.getHeaderString(HttpHeaders.CONTENT_DISPOSITION);
+        assertNotNull(contentDisposition);
+
+        Object entity = response.getEntity();
+        assertTrue(entity instanceof InputStream);
+        String configExport = IOUtils.toString((InputStream) entity, SyncopeConstants.DEFAULT_ENCODING);
+        assertFalse(configExport.isEmpty());
+        assertTrue(configExport.length() > 1000);
+    }
+
+    @Test
+    public void issueSYNCOPE418() {
+        PlainSchemaTO failing = new PlainSchemaTO();
+        failing.setKey("http://schemas.examples.org/security/authorization/organizationUnit");
+        failing.setType(AttrSchemaType.String);
+
+        try {
+            createSchema(AttributableType.CONFIGURATION, SchemaType.PLAIN, failing);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidPlainSchema, e.getType());
+
+            assertNotNull(e.getElements());
+            assertEquals(1, e.getElements().size());
+            assertTrue(e.getElements().iterator().next().contains(EntityViolationType.InvalidName.name()));
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE629() throws IOException {
+        PlainSchemaTO membershipKey = new PlainSchemaTO();
+        membershipKey.setKey("membershipKey" + getUUIDString());
+        membershipKey.setType(AttrSchemaType.String);
+        createSchema(AttributableType.MEMBERSHIP, SchemaType.PLAIN, membershipKey);
+
+        PlainSchemaTO roleKey = new PlainSchemaTO();
+        roleKey.setKey("roleKey" + getUUIDString());
+        roleKey.setType(AttrSchemaType.String);
+        createSchema(AttributableType.ROLE, SchemaType.PLAIN, roleKey);
+
+        RoleTO roleTO = new RoleTO();
+        roleTO.setName("aRole" + getUUIDString());
+        roleTO.getMAttrTemplates().add(membershipKey.getKey());
+        roleTO.getRAttrTemplates().add(roleKey.getKey());
+        roleTO = createRole(roleTO);
+
+        try {
+            Response response = configurationService.export();
+            assertNotNull(response);
+            assertEquals(Response.Status.OK.getStatusCode(), response.getStatusInfo().getStatusCode());
+            assertTrue(response.getMediaType().toString().startsWith(MediaType.TEXT_XML));
+            String contentDisposition = response.getHeaderString(HttpHeaders.CONTENT_DISPOSITION);
+            assertNotNull(contentDisposition);
+
+            Object entity = response.getEntity();
+            assertTrue(entity instanceof InputStream);
+            String configExport = IOUtils.toString((InputStream) entity, SyncopeConstants.DEFAULT_ENCODING);
+            assertFalse(configExport.isEmpty());
+            assertTrue(configExport.length() > 1000);
+
+            String[] result = StringUtils.substringsBetween(configExport, "<RPLAINATTRTEMPLATE", "/>");
+            assertNotNull(result);
+            boolean rattrExists = false;
+            for (String entry : result) {
+                if (entry.contains(roleKey.getKey())) {
+                    rattrExists = true;
+                }
+            }
+            assertTrue(rattrExists);
+
+            result = StringUtils.substringsBetween(configExport, "<MPLAINATTRTEMPLATE", "/>");
+            assertNotNull(result);
+            boolean mattrExists = false;
+            for (String entry : result) {
+                if (entry.contains(membershipKey.getKey())) {
+                    mattrExists = true;
+                }
+            }
+            assertTrue(mattrExists);
+        } finally {
+            deleteRole(roleTO.getKey());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ConnectorITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ConnectorITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ConnectorITCase.java
new file mode 100644
index 0000000..e7d12d9
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ConnectorITCase.java
@@ -0,0 +1,723 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+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.io.IOException;
+import java.io.InputStream;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import javax.ws.rs.core.Response;
+import org.apache.commons.io.IOUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.BulkAction;
+import org.apache.syncope.common.lib.to.ConnBundleTO;
+import org.apache.syncope.common.lib.to.ConnIdObjectClassTO;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.lib.to.ConnPoolConfTO;
+import org.apache.syncope.common.lib.to.MappingItemTO;
+import org.apache.syncope.common.lib.to.MappingTO;
+import org.apache.syncope.common.lib.to.PlainSchemaTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.types.ConnConfPropSchema;
+import org.apache.syncope.common.lib.types.ConnConfProperty;
+import org.apache.syncope.common.lib.types.ConnectorCapability;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.rest.api.service.ConnectorService;
+import org.apache.syncope.common.rest.api.service.ResourceService;
+import org.identityconnectors.common.security.GuardedString;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class ConnectorITCase extends AbstractITCase {
+
+    private static String connectorServerLocation;
+
+    private static String connidSoapVersion;
+
+    private static String connidDbTableVersion;
+
+    private static String testJDBCURL;
+
+    @BeforeClass
+    public static void setUpConnIdBundles() throws IOException {
+        InputStream propStream = null;
+        try {
+            Properties props = new Properties();
+            propStream = ConnectorITCase.class.getResourceAsStream("/connid.properties");
+            props.load(propStream);
+
+            for (String location : props.getProperty("connid.locations").split(",")) {
+                if (!location.startsWith("file")) {
+                    connectorServerLocation = location;
+                }
+            }
+
+            connidSoapVersion = props.getProperty("connid.soap.version");
+            connidDbTableVersion = props.getProperty("connid.db.table.version");
+
+            testJDBCURL = props.getProperty("testdb.url");
+        } catch (Exception e) {
+            LOG.error("Could not load /connid.properties", e);
+        } finally {
+            IOUtils.closeQuietly(propStream);
+        }
+        assertNotNull(connectorServerLocation);
+        assertNotNull(connidSoapVersion);
+        assertNotNull(connidDbTableVersion);
+        assertNotNull(testJDBCURL);
+    }
+
+    @Test(expected = SyncopeClientException.class)
+    public void createWithException() {
+        ConnInstanceTO connectorTO = new ConnInstanceTO();
+
+        Response response = connectorService.create(connectorTO);
+        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+            throw (RuntimeException) clientFactory.getExceptionMapper().fromResponse(response);
+        }
+    }
+
+    @Test
+    public void create() {
+        ConnInstanceTO connectorTO = new ConnInstanceTO();
+        connectorTO.setLocation(connectorService.read(100L).getLocation());
+        connectorTO.setVersion(connidSoapVersion);
+        connectorTO.setConnectorName("net.tirasa.connid.bundles.soap.WebServiceConnector");
+        connectorTO.setBundleName("net.tirasa.connid.bundles.soap");
+        connectorTO.setDisplayName("Display name");
+        connectorTO.setConnRequestTimeout(15);
+
+        // set the connector configuration using PropertyTO
+        Set<ConnConfProperty> conf = new HashSet<>();
+
+        ConnConfPropSchema endpointSchema = new ConnConfPropSchema();
+        endpointSchema.setName("endpoint");
+        endpointSchema.setType(String.class.getName());
+        endpointSchema.setRequired(true);
+        ConnConfProperty endpoint = new ConnConfProperty();
+        endpoint.setSchema(endpointSchema);
+        endpoint.getValues().add("http://localhost:8888/wssample/services");
+        endpoint.getValues().add("Provisioning");
+        conf.add(endpoint);
+
+        ConnConfPropSchema servicenameSchema = new ConnConfPropSchema();
+        servicenameSchema.setName("servicename");
+        servicenameSchema.setType(String.class.getName());
+        servicenameSchema.setRequired(true);
+        ConnConfProperty servicename = new ConnConfProperty();
+        servicename.setSchema(servicenameSchema);
+        conf.add(servicename);
+
+        // set connector configuration
+        connectorTO.getConfiguration().addAll(conf);
+
+        // set connector capabilities
+        connectorTO.getCapabilities().add(ConnectorCapability.TWO_PHASES_CREATE);
+        connectorTO.getCapabilities().add(ConnectorCapability.ONE_PHASE_CREATE);
+        connectorTO.getCapabilities().add(ConnectorCapability.TWO_PHASES_UPDATE);
+
+        // set connector pool conf
+        ConnPoolConfTO cpc = new ConnPoolConfTO();
+        cpc.setMaxObjects(1534);
+        connectorTO.setPoolConf(cpc);
+
+        Response response = connectorService.create(connectorTO);
+        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+            throw (RuntimeException) clientFactory.getExceptionMapper().fromResponse(response);
+        }
+
+        ConnInstanceTO actual = getObject(
+                response.getLocation(), ConnectorService.class, ConnInstanceTO.class);
+        assertNotNull(actual);
+
+        assertEquals(actual.getBundleName(), connectorTO.getBundleName());
+        assertEquals(actual.getConnectorName(), connectorTO.getConnectorName());
+        assertEquals(actual.getVersion(), connectorTO.getVersion());
+        assertEquals("Display name", actual.getDisplayName());
+        assertEquals(Integer.valueOf(15), actual.getConnRequestTimeout());
+        assertEquals(connectorTO.getCapabilities(), actual.getCapabilities());
+        assertNotNull(actual.getPoolConf());
+        assertEquals(1534, actual.getPoolConf().getMaxObjects().intValue());
+        assertEquals(10, actual.getPoolConf().getMaxIdle().intValue());
+
+        Throwable t = null;
+
+        // check update
+        actual.getCapabilities().remove(ConnectorCapability.TWO_PHASES_UPDATE);
+        actual.getPoolConf().setMaxObjects(null);
+
+        try {
+            connectorService.update(actual.getKey(), actual);
+            actual = connectorService.read(actual.getKey());
+        } catch (SyncopeClientException e) {
+            LOG.error("update failed", e);
+            t = e;
+        }
+
+        assertNull(t);
+        assertNotNull(actual);
+        assertEquals(EnumSet.of(ConnectorCapability.ONE_PHASE_CREATE, ConnectorCapability.TWO_PHASES_CREATE),
+                actual.getCapabilities());
+        assertEquals(10, actual.getPoolConf().getMaxObjects().intValue());
+
+        // check also for the deletion of the created object
+        try {
+            connectorService.delete(actual.getKey());
+        } catch (SyncopeClientException e) {
+            LOG.error("delete failed", e);
+            t = e;
+        }
+
+        assertNull(t);
+
+        // check the non existence
+        try {
+            connectorService.read(actual.getKey());
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
+        }
+    }
+
+    @Test
+    public void update() {
+        ConnInstanceTO connectorTO = new ConnInstanceTO();
+
+        // set connector instance id
+        connectorTO.setKey(103L);
+
+        // set connector version
+        connectorTO.setVersion(connidSoapVersion);
+
+        // set connector name
+        connectorTO.setConnectorName("net.tirasa.connid.bundles.soap.WebServiceConnector");
+
+        // set bundle name
+        connectorTO.setBundleName("net.tirasa.connid.bundles.soap");
+
+        connectorTO.setConnRequestTimeout(20);
+
+        // set the connector configuration using PropertyTO
+        Set<ConnConfProperty> conf = new HashSet<ConnConfProperty>();
+
+        ConnConfPropSchema endpointSchema = new ConnConfPropSchema();
+        endpointSchema.setName("endpoint");
+        endpointSchema.setType(String.class.getName());
+        endpointSchema.setRequired(true);
+        ConnConfProperty endpoint = new ConnConfProperty();
+        endpoint.setSchema(endpointSchema);
+        endpoint.getValues().add("http://localhost:8888/wssample/services");
+        conf.add(endpoint);
+
+        ConnConfPropSchema servicenameSchema = new ConnConfPropSchema();
+        servicenameSchema.setName("servicename");
+        servicenameSchema.setType(String.class.getName());
+        servicenameSchema.setRequired(true);
+        ConnConfProperty servicename = new ConnConfProperty();
+        servicename.setSchema(servicenameSchema);
+        servicename.getValues().add("Provisioning");
+        conf.add(servicename);
+
+        // set connector configuration
+        connectorTO.getConfiguration().addAll(conf);
+
+        connectorService.update(connectorTO.getKey(), connectorTO);
+        ConnInstanceTO actual = connectorService.read(connectorTO.getKey());
+
+        assertNotNull(actual);
+
+        actual = connectorService.read(actual.getKey());
+
+        assertNotNull(actual);
+        assertEquals(actual.getBundleName(), connectorTO.getBundleName());
+        assertEquals(actual.getConnectorName(), connectorTO.getConnectorName());
+        assertEquals(actual.getVersion(), connectorTO.getVersion());
+        assertEquals(Integer.valueOf(20), actual.getConnRequestTimeout());
+    }
+
+    @Test
+    public void issueSYNCOPE10() {
+        // ----------------------------------
+        // Copy resource and connector in order to create new objects.
+        // ----------------------------------
+        // Retrieve a connector instance template.
+        ConnInstanceTO connInstanceTO = connectorService.read(103L);
+
+        assertNotNull(connInstanceTO);
+
+        // check for resource
+        List<ResourceTO> resources = resourceService.list(Long.valueOf(103));
+
+        assertEquals(4, resources.size());
+
+        // Retrieve a resource TO template.
+        ResourceTO resourceTO = resources.get(0);
+
+        // Make it new.
+        resourceTO.setKey("newAbout103");
+
+        // Make it new.
+        connInstanceTO.setKey(0);
+        connInstanceTO.setDisplayName("newDisplayName" + getUUIDString());
+        // ----------------------------------
+
+        // ----------------------------------
+        // Create a new connector instance.
+        // ----------------------------------
+        Response response = connectorService.create(connInstanceTO);
+        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+            throw (RuntimeException) clientFactory.getExceptionMapper().fromResponse(response);
+        }
+
+        connInstanceTO = getObject(response.getLocation(), ConnectorService.class, ConnInstanceTO.class);
+        assertNotNull(connInstanceTO);
+        assertTrue(connInstanceTO.getCapabilities().isEmpty());
+
+        long connId = connInstanceTO.getKey();
+
+        // Link resourceTO to the new connector instance.
+        resourceTO.setConnectorId(connId);
+        // ----------------------------------
+
+        // ----------------------------------
+        // Check for connector instance update after resource creation.
+        // ----------------------------------
+        response = resourceService.create(resourceTO);
+        resourceTO = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+
+        assertNotNull(resourceTO);
+
+        resources = resourceService.list(connId);
+
+        assertEquals(1, resources.size());
+        // ----------------------------------
+
+        // ----------------------------------
+        // Check for spring bean.
+        // ----------------------------------
+        ConnInstanceTO connInstanceBean = connectorService.readByResource(resourceTO.getKey());
+
+        assertNotNull(connInstanceBean);
+        assertTrue(connInstanceBean.getCapabilities().isEmpty());
+        // ----------------------------------
+
+        // ----------------------------------
+        // Check for spring bean update after connector instance update.
+        // ----------------------------------
+        connInstanceTO.getCapabilities().add(ConnectorCapability.SEARCH);
+
+        connectorService.update(connInstanceTO.getKey(), connInstanceTO);
+        ConnInstanceTO actual = connectorService.read(connInstanceTO.getKey());
+
+        assertNotNull(actual);
+        assertFalse(connInstanceTO.getCapabilities().isEmpty());
+
+        // check for spring bean update
+        connInstanceBean = connectorService.readByResource(resourceTO.getKey());
+
+        assertFalse(connInstanceBean.getCapabilities().isEmpty());
+        // ----------------------------------
+    }
+
+    @Test
+    public void deleteWithException() {
+        try {
+            connectorService.delete(0L);
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
+        }
+    }
+
+    @Test
+    public void list() {
+        List<ConnInstanceTO> connectorInstanceTOs = connectorService.list(null);
+        assertNotNull(connectorInstanceTOs);
+        assertFalse(connectorInstanceTOs.isEmpty());
+        for (ConnInstanceTO instance : connectorInstanceTOs) {
+            assertNotNull(instance);
+        }
+    }
+
+    @Test
+    public void read() {
+        ConnInstanceTO connectorInstanceTO = connectorService.read(100L);
+        assertNotNull(connectorInstanceTO);
+    }
+
+    @Test
+    public void getBundles() {
+        List<ConnBundleTO> bundles = connectorService.getBundles(null);
+        assertNotNull(bundles);
+        assertFalse(bundles.isEmpty());
+        for (ConnBundleTO bundle : bundles) {
+            assertNotNull(bundle);
+        }
+    }
+
+    @Test
+    public void getConnectorConfiguration() {
+        List<ConnConfProperty> props = connectorService.getConfigurationProperties(104L);
+        assertNotNull(props);
+        assertFalse(props.isEmpty());
+    }
+
+    @Test
+    public void checkHiddenProperty() {
+        ConnInstanceTO connInstanceTO = connectorService.read(100L);
+
+        boolean check = false;
+
+        for (ConnConfProperty prop : connInstanceTO.getConfiguration()) {
+            if ("receiveTimeout".equals(prop.getSchema().getName())) {
+                check = true;
+            }
+        }
+        assertTrue(check);
+    }
+
+    @Test
+    public void checkSelectedLanguage() {
+        // 1. Check Italian
+        List<ConnInstanceTO> connectorInstanceTOs = connectorService.list("it");
+
+        Map<String, ConnConfProperty> instanceConfMap;
+        for (ConnInstanceTO instance : connectorInstanceTOs) {
+            if ("net.tirasa.connid.bundles.db.table".equals(instance.getBundleName())) {
+                instanceConfMap = instance.getConfigurationMap();
+                assertEquals("Utente", instanceConfMap.get("user").getSchema().getDisplayName());
+            }
+        }
+
+        // 2. Check English (default)
+        connectorInstanceTOs = connectorService.list(null);
+
+        for (ConnInstanceTO instance : connectorInstanceTOs) {
+            if ("net.tirasa.connid.bundles.db.table".equals(instance.getBundleName())) {
+                instanceConfMap = instance.getConfigurationMap();
+                assertEquals("User", instanceConfMap.get("user").getSchema().getDisplayName());
+            }
+        }
+    }
+
+    @Test
+    public void validate() {
+        ConnInstanceTO connectorTO = new ConnInstanceTO();
+        connectorTO.setLocation(connectorServerLocation);
+        connectorTO.setVersion(connidDbTableVersion);
+        connectorTO.setConnectorName("net.tirasa.connid.bundles.db.table.DatabaseTableConnector");
+        connectorTO.setBundleName("net.tirasa.connid.bundles.db.table");
+        connectorTO.setDisplayName("H2Test");
+
+        // set the connector configuration using PropertyTO
+        Set<ConnConfProperty> conf = new HashSet<ConnConfProperty>();
+
+        ConnConfPropSchema jdbcDriverSchema = new ConnConfPropSchema();
+        jdbcDriverSchema.setName("jdbcDriver");
+        jdbcDriverSchema.setType(String.class.getName());
+        jdbcDriverSchema.setRequired(true);
+        ConnConfProperty jdbcDriver = new ConnConfProperty();
+        jdbcDriver.setSchema(jdbcDriverSchema);
+        jdbcDriver.getValues().add("org.h2.Driver");
+        conf.add(jdbcDriver);
+
+        ConnConfPropSchema jdbcUrlTemplateSchema = new ConnConfPropSchema();
+        jdbcUrlTemplateSchema.setName("jdbcUrlTemplate");
+        jdbcUrlTemplateSchema.setType(String.class.getName());
+        jdbcUrlTemplateSchema.setRequired(true);
+        ConnConfProperty jdbcUrlTemplate = new ConnConfProperty();
+        jdbcUrlTemplate.setSchema(jdbcUrlTemplateSchema);
+        jdbcUrlTemplate.getValues().add(testJDBCURL);
+        conf.add(jdbcUrlTemplate);
+
+        ConnConfPropSchema userSchema = new ConnConfPropSchema();
+        userSchema.setName("user");
+        userSchema.setType(String.class.getName());
+        userSchema.setRequired(false);
+        ConnConfProperty user = new ConnConfProperty();
+        user.setSchema(userSchema);
+        user.getValues().add("sa");
+        conf.add(user);
+
+        ConnConfPropSchema passwordSchema = new ConnConfPropSchema();
+        passwordSchema.setName("password");
+        passwordSchema.setType(GuardedString.class.getName());
+        passwordSchema.setRequired(true);
+        ConnConfProperty password = new ConnConfProperty();
+        password.setSchema(passwordSchema);
+        password.getValues().add("sa");
+        conf.add(password);
+
+        ConnConfPropSchema tableSchema = new ConnConfPropSchema();
+        tableSchema.setName("table");
+        tableSchema.setType(String.class.getName());
+        tableSchema.setRequired(true);
+        ConnConfProperty table = new ConnConfProperty();
+        table.setSchema(tableSchema);
+        table.getValues().add("test");
+        conf.add(table);
+
+        ConnConfPropSchema keyColumnSchema = new ConnConfPropSchema();
+        keyColumnSchema.setName("keyColumn");
+        keyColumnSchema.setType(String.class.getName());
+        keyColumnSchema.setRequired(true);
+        ConnConfProperty keyColumn = new ConnConfProperty();
+        keyColumn.setSchema(keyColumnSchema);
+        keyColumn.getValues().add("id");
+        conf.add(keyColumn);
+
+        ConnConfPropSchema passwordColumnSchema = new ConnConfPropSchema();
+        passwordColumnSchema.setName("passwordColumn");
+        passwordColumnSchema.setType(String.class.getName());
+        passwordColumnSchema.setRequired(true);
+        ConnConfProperty passwordColumn = new ConnConfProperty();
+        passwordColumn.setSchema(passwordColumnSchema);
+        passwordColumn.getValues().add("password");
+        conf.add(passwordColumn);
+
+        // set connector configuration
+        connectorTO.getConfiguration().addAll(conf);
+
+        assertTrue(connectorService.check(connectorTO));
+
+        conf.remove(password);
+        password.getValues().clear();
+        password.getValues().add("password");
+        conf.add(password);
+
+        assertFalse(connectorService.check(connectorTO));
+    }
+
+    @Test
+    public void getSchemaNames() {
+        ConnInstanceTO conn = connectorService.read(101L);
+
+        List<PlainSchemaTO> schemaNames = connectorService.getSchemaNames(conn.getKey(), conn, true);
+        assertNotNull(schemaNames);
+        assertFalse(schemaNames.isEmpty());
+        assertNotNull(schemaNames.get(0).getKey());
+        assertNull(schemaNames.get(0).getEnumerationValues());
+
+        schemaNames = connectorService.getSchemaNames(conn.getKey(), conn, false);
+
+        assertNotNull(schemaNames);
+        assertEquals(0, schemaNames.size());
+
+        conn = connectorService.read(104L);
+
+        // to be used with overridden properties
+        conn.getConfiguration().clear();
+
+        schemaNames = connectorService.getSchemaNames(conn.getKey(), conn, true);
+        assertNotNull(schemaNames);
+        assertFalse(schemaNames.isEmpty());
+    }
+
+    @Test
+    public void getSupportedObjectClasses() {
+        ConnInstanceTO ldap = connectorService.read(105L);
+        assertNotNull(ldap);
+
+        List<ConnIdObjectClassTO> objectClasses = connectorService.getSupportedObjectClasses(ldap.getKey(), ldap);
+        assertNotNull(objectClasses);
+        assertEquals(2, objectClasses.size());
+        assertTrue(objectClasses.contains(ConnIdObjectClassTO.ACCOUNT));
+        assertTrue(objectClasses.contains(ConnIdObjectClassTO.GROUP));
+
+        ConnInstanceTO csv = connectorService.read(104L);
+        assertNotNull(csv);
+
+        objectClasses = connectorService.getSupportedObjectClasses(csv.getKey(), csv);
+        assertNotNull(objectClasses);
+        assertEquals(1, objectClasses.size());
+        assertTrue(objectClasses.contains(ConnIdObjectClassTO.ACCOUNT));
+    }
+
+    @Test
+    public void issueSYNCOPE112() {
+        // ----------------------------------------
+        // Create a new connector
+        // ----------------------------------------
+        ConnInstanceTO connectorTO = new ConnInstanceTO();
+
+        connectorTO.setLocation(connectorService.read(100L).getLocation());
+
+        // set connector version
+        connectorTO.setVersion(connidSoapVersion);
+
+        // set connector name
+        connectorTO.setConnectorName("net.tirasa.connid.bundles.soap.WebServiceConnector");
+
+        // set bundle name
+        connectorTO.setBundleName("net.tirasa.connid.bundles.soap");
+
+        // set display name
+        connectorTO.setDisplayName("WSSoap");
+
+        // set the connector configuration using PropertyTO
+        Set<ConnConfProperty> conf = new HashSet<ConnConfProperty>();
+
+        ConnConfPropSchema userSchema = new ConnConfPropSchema();
+        userSchema.setName("endpoint");
+        userSchema.setType(String.class.getName());
+        userSchema.setRequired(true);
+        ConnConfProperty endpoint = new ConnConfProperty();
+        endpoint.setSchema(userSchema);
+        endpoint.getValues().add("http://localhost:9080/does_not_work");
+        endpoint.setOverridable(true);
+
+        ConnConfPropSchema keyColumnSchema = new ConnConfPropSchema();
+        keyColumnSchema.setName("servicename");
+        keyColumnSchema.setType(String.class.getName());
+        keyColumnSchema.setRequired(true);
+        ConnConfProperty servicename = new ConnConfProperty();
+        servicename.setSchema(keyColumnSchema);
+        servicename.getValues().add("net.tirasa.connid.bundles.soap.provisioning.interfaces.Provisioning");
+        servicename.setOverridable(false);
+
+        conf.add(endpoint);
+        conf.add(servicename);
+
+        // set connector configuration
+        connectorTO.getConfiguration().addAll(conf);
+
+        try {
+            assertFalse(connectorService.check(connectorTO));
+
+            Response response = connectorService.create(connectorTO);
+            if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+                throw (RuntimeException) clientFactory.getExceptionMapper().fromResponse(response);
+            }
+
+            connectorTO = getObject(response.getLocation(), ConnectorService.class, ConnInstanceTO.class);
+            assertNotNull(connectorTO);
+            // ----------------------------------------
+
+            // ----------------------------------------
+            // create a resourceTO
+            // ----------------------------------------
+            String resourceName = "checkForPropOverriding";
+            ResourceTO resourceTO = new ResourceTO();
+
+            resourceTO.setKey(resourceName);
+            resourceTO.setConnectorId(connectorTO.getKey());
+
+            conf = new HashSet<ConnConfProperty>();
+            endpoint.getValues().clear();
+            endpoint.getValues().add("http://localhost:9080/wssample/services/provisioning");
+            conf.add(endpoint);
+
+            resourceTO.getConnConfProperties().addAll(conf);
+
+            MappingTO mapping = new MappingTO();
+            resourceTO.setUmapping(mapping);
+
+            MappingItemTO mapItem = new MappingItemTO();
+            mapItem.setExtAttrName("uid");
+            mapItem.setIntAttrName("userId");
+            mapItem.setIntMappingType(IntMappingType.UserPlainSchema);
+            mapItem.setAccountid(true);
+            mapping.setAccountIdItem(mapItem);
+            // ----------------------------------------
+
+            // ----------------------------------------
+            // Check connection without saving the resource ....
+            // ----------------------------------------
+            assertTrue(resourceService.check(resourceTO));
+            // ----------------------------------------
+        } finally {
+            // Remove connector from db to make test re-runnable
+            connectorService.delete(connectorTO.getKey());
+        }
+    }
+
+    @Test
+    public void reload() {
+        connectorService.reload();
+    }
+
+    @Test
+    public void bulkAction() {
+        final BulkAction bulkAction = new BulkAction();
+        bulkAction.setOperation(BulkAction.Type.DELETE);
+
+        ConnInstanceTO conn = connectorService.read(101L);
+
+        conn.setKey(0);
+        conn.setDisplayName("forBulk1");
+
+        bulkAction.getTargets().add(String.valueOf(getObject(
+                connectorService.create(conn).getLocation(), ConnectorService.class, ConnInstanceTO.class).getKey()));
+
+        conn.setDisplayName("forBulk2");
+
+        bulkAction.getTargets().add(String.valueOf(getObject(
+                connectorService.create(conn).getLocation(), ConnectorService.class, ConnInstanceTO.class).getKey()));
+
+        Iterator<String> iter = bulkAction.getTargets().iterator();
+
+        assertNotNull(connectorService.read(Long.valueOf(iter.next())));
+        assertNotNull(connectorService.read(Long.valueOf(iter.next())));
+
+        connectorService.bulk(bulkAction);
+
+        iter = bulkAction.getTargets().iterator();
+
+        try {
+            connectorService.read(Long.valueOf(iter.next()));
+            fail();
+        } catch (SyncopeClientException e) {
+            assertNotNull(e);
+        }
+
+        try {
+            connectorService.read(Long.valueOf(iter.next()));
+            fail();
+        } catch (SyncopeClientException e) {
+            assertNotNull(e);
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE605() {
+
+        ConnInstanceTO connectorInstanceTO = connectorService.read(103L);
+        assertTrue(connectorInstanceTO.getCapabilities().isEmpty());
+
+        connectorInstanceTO.getCapabilities().add(ConnectorCapability.SEARCH);
+        connectorService.update(connectorInstanceTO.getKey(), connectorInstanceTO);
+
+        ConnInstanceTO updatedCapabilities = connectorService.read(connectorInstanceTO.getKey());
+        assertNotNull(updatedCapabilities.getCapabilities());
+        assertTrue(updatedCapabilities.getCapabilities().size() == 1);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/DerSchemaITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/DerSchemaITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/DerSchemaITCase.java
new file mode 100644
index 0000000..9ac24ea
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/DerSchemaITCase.java
@@ -0,0 +1,151 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.DerSchemaTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+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.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class DerSchemaITCase extends AbstractITCase {
+
+    @Test
+    public void list() {
+        List<DerSchemaTO> derivedSchemas = schemaService.list(AttributableType.USER, SchemaType.DERIVED);
+        assertFalse(derivedSchemas.isEmpty());
+        for (DerSchemaTO derivedSchemaTO : derivedSchemas) {
+            assertNotNull(derivedSchemaTO);
+        }
+    }
+
+    @Test
+    public void read() {
+        DerSchemaTO derivedSchemaTO = schemaService.read(AttributableType.USER, SchemaType.DERIVED,
+                "cn");
+        assertNotNull(derivedSchemaTO);
+    }
+
+    @Test
+    public void create() {
+        DerSchemaTO schema = new DerSchemaTO();
+        schema.setKey("derived");
+        schema.setExpression("derived_sx + '_' + derived_dx");
+
+        DerSchemaTO actual = createSchema(AttributableType.USER, SchemaType.DERIVED, schema);
+        assertNotNull(actual);
+
+        actual = schemaService.read(AttributableType.USER, SchemaType.DERIVED, actual.getKey());
+        assertNotNull(actual);
+        assertEquals(actual.getExpression(), "derived_sx + '_' + derived_dx");
+    }
+
+    @Test
+    public void delete() {
+        DerSchemaTO schema = schemaService.read(AttributableType.ROLE, SchemaType.DERIVED, "rderiveddata");
+        assertNotNull(schema);
+
+        schemaService.delete(AttributableType.ROLE, SchemaType.DERIVED, schema.getKey());
+
+        try {
+            schemaService.read(AttributableType.ROLE, SchemaType.DERIVED, "rderiveddata");
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+        } finally {
+            // Recreate schema to make test re-runnable
+            schema = createSchema(AttributableType.ROLE, SchemaType.DERIVED, schema);
+            assertNotNull(schema);
+        }
+    }
+
+    @Test
+    public void update() {
+        DerSchemaTO schema = schemaService.read(AttributableType.MEMBERSHIP, SchemaType.DERIVED,
+                "mderiveddata");
+        assertNotNull(schema);
+        assertEquals("mderived_sx + '-' + mderived_dx", schema.getExpression());
+        try {
+            schema.setExpression("mderived_sx + '.' + mderived_dx");
+
+            schemaService.update(AttributableType.MEMBERSHIP, SchemaType.DERIVED,
+                    schema.getKey(), schema);
+
+            schema = schemaService.read(AttributableType.MEMBERSHIP, SchemaType.DERIVED, "mderiveddata");
+            assertNotNull(schema);
+            assertEquals("mderived_sx + '.' + mderived_dx", schema.getExpression());
+        } finally {
+            // Set updated back to make test re-runnable
+            schema.setExpression("mderived_sx + '-' + mderived_dx");
+            schemaService.update(AttributableType.MEMBERSHIP, SchemaType.DERIVED,
+                    schema.getKey(), schema);
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE323() {
+        DerSchemaTO actual = schemaService.read(AttributableType.ROLE, SchemaType.DERIVED, "rderiveddata");
+        assertNotNull(actual);
+
+        try {
+            createSchema(AttributableType.ROLE, SchemaType.DERIVED, actual);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.CONFLICT, e.getType().getResponseStatus());
+            assertEquals(ClientExceptionType.EntityExists, e.getType());
+        }
+
+        actual.setKey(null);
+        try {
+            createSchema(AttributableType.ROLE, SchemaType.DERIVED, actual);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.BAD_REQUEST, e.getType().getResponseStatus());
+            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE418() {
+        DerSchemaTO schema = new DerSchemaTO();
+        schema.setKey("http://schemas.examples.org/security/authorization/organizationUnit");
+        schema.setExpression("derived_sx + '_' + derived_dx");
+
+        try {
+            createSchema(AttributableType.ROLE, SchemaType.DERIVED, schema);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidDerSchema, e.getType());
+            assertTrue(e.getElements().iterator().next().contains(EntityViolationType.InvalidName.name()));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/LoggerITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/LoggerITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/LoggerITCase.java
new file mode 100644
index 0000000..68510bf
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/LoggerITCase.java
@@ -0,0 +1,215 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import java.text.ParseException;
+import java.util.List;
+import org.apache.syncope.common.lib.to.EventCategoryTO;
+import org.apache.syncope.common.lib.to.LoggerTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditElements.EventCategoryType;
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+import org.apache.syncope.common.lib.types.LoggerLevel;
+import org.apache.syncope.common.lib.types.LoggerType;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.common.rest.api.CollectionWrapper;
+import org.apache.syncope.server.logic.ReportLogic;
+import org.apache.syncope.server.logic.ResourceLogic;
+import org.apache.syncope.server.logic.RoleLogic;
+import org.apache.syncope.server.logic.UserLogic;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class LoggerITCase extends AbstractITCase {
+
+    @Test
+    public void listLogs() {
+        List<LoggerTO> loggers = loggerService.list(LoggerType.LOG);
+        assertNotNull(loggers);
+        assertFalse(loggers.isEmpty());
+        for (LoggerTO logger : loggers) {
+            assertNotNull(logger);
+        }
+    }
+
+    @Test
+    public void listAudits() throws ParseException {
+        List<LoggerTO> audits = loggerService.list(LoggerType.AUDIT);
+
+        assertNotNull(audits);
+        assertFalse(audits.isEmpty());
+        for (LoggerTO audit : audits) {
+            assertNotNull(AuditLoggerName.fromLoggerName(audit.getKey()));
+        }
+    }
+
+    @Test
+    public void setLevel() {
+        List<LoggerTO> loggers = loggerService.list(LoggerType.LOG);
+        assertNotNull(loggers);
+        int startSize = loggers.size();
+
+        LoggerTO logger = new LoggerTO();
+        logger.setKey("TEST");
+        logger.setLevel(LoggerLevel.INFO);
+        loggerService.update(LoggerType.LOG, logger.getKey(), logger);
+        logger = loggerService.read(LoggerType.LOG, logger.getKey());
+        assertNotNull(logger);
+        assertEquals(LoggerLevel.INFO, logger.getLevel());
+
+        loggers = loggerService.list(LoggerType.LOG);
+        assertNotNull(loggers);
+        assertEquals(startSize + 1, loggers.size());
+
+        // TEST Delete
+        loggerService.delete(LoggerType.LOG, "TEST");
+        loggers = loggerService.list(LoggerType.LOG);
+        assertNotNull(loggers);
+        assertEquals(startSize, loggers.size());
+    }
+
+    @Test
+    public void enableDisableAudit() {
+        AuditLoggerName auditLoggerName = new AuditLoggerName(
+                EventCategoryType.REST,
+                ReportLogic.class.getSimpleName(),
+                null,
+                "deleteExecution",
+                AuditElements.Result.FAILURE);
+
+        List<AuditLoggerName> audits = CollectionWrapper.wrapLogger(loggerService.list(LoggerType.AUDIT));
+        assertNotNull(audits);
+        assertFalse(audits.contains(auditLoggerName));
+
+        LoggerTO loggerTO = new LoggerTO();
+        String name = auditLoggerName.toLoggerName();
+        loggerTO.setKey(name);
+        loggerTO.setLevel(LoggerLevel.DEBUG);
+        loggerService.update(LoggerType.AUDIT, name, loggerTO);
+
+        audits = CollectionWrapper.wrapLogger(loggerService.list(LoggerType.AUDIT));
+        assertNotNull(audits);
+        assertTrue(audits.contains(auditLoggerName));
+
+        loggerService.delete(LoggerType.AUDIT, auditLoggerName.toLoggerName());
+
+        audits = CollectionWrapper.wrapLogger(loggerService.list(LoggerType.AUDIT));
+        assertNotNull(audits);
+        assertFalse(audits.contains(auditLoggerName));
+    }
+
+    @Test
+    public void listAuditEvents() {
+        final List<EventCategoryTO> events = loggerService.events();
+
+        boolean found = false;
+
+        for (EventCategoryTO eventCategoryTO : events) {
+            if (UserLogic.class.getSimpleName().equals(eventCategoryTO.getCategory())) {
+                assertEquals(EventCategoryType.REST, eventCategoryTO.getType());
+                assertTrue(eventCategoryTO.getEvents().contains("create"));
+                assertTrue(eventCategoryTO.getEvents().contains("list"));
+                assertFalse(eventCategoryTO.getEvents().contains("doCreate"));
+                assertFalse(eventCategoryTO.getEvents().contains("setStatusOnWfAdapter"));
+                assertFalse(eventCategoryTO.getEvents().contains("resolveReference"));
+                found = true;
+            }
+        }
+        assertTrue(found);
+
+        found = false;
+        for (EventCategoryTO eventCategoryTO : events) {
+            if (RoleLogic.class.getSimpleName().equals(eventCategoryTO.getCategory())) {
+                assertEquals(EventCategoryType.REST, eventCategoryTO.getType());
+                assertTrue(eventCategoryTO.getEvents().contains("create"));
+                assertTrue(eventCategoryTO.getEvents().contains("list"));
+                assertFalse(eventCategoryTO.getEvents().contains("resolveReference"));
+                found = true;
+            }
+        }
+        assertTrue(found);
+
+        found = false;
+        for (EventCategoryTO eventCategoryTO : events) {
+            if (ResourceLogic.class.getSimpleName().equals(eventCategoryTO.getCategory())) {
+                assertEquals(EventCategoryType.REST, eventCategoryTO.getType());
+                assertTrue(eventCategoryTO.getEvents().contains("create"));
+                assertTrue(eventCategoryTO.getEvents().contains("read"));
+                assertTrue(eventCategoryTO.getEvents().contains("delete"));
+                assertFalse(eventCategoryTO.getEvents().contains("resolveReference"));
+                found = true;
+            }
+        }
+        assertTrue(found);
+
+        found = false;
+        for (EventCategoryTO eventCategoryTO : events) {
+            if (AttributableType.USER.name().toLowerCase().equals(eventCategoryTO.getCategory())) {
+                if (RESOURCE_NAME_LDAP.equals(eventCategoryTO.getSubcategory())
+                        && EventCategoryType.SYNCHRONIZATION == eventCategoryTO.getType()) {
+                    assertTrue(eventCategoryTO.getEvents().contains(ResourceOperation.CREATE.name().toLowerCase()));
+                    assertTrue(eventCategoryTO.getEvents().contains(ResourceOperation.UPDATE.name().toLowerCase()));
+                    assertTrue(eventCategoryTO.getEvents().contains(ResourceOperation.DELETE.name().toLowerCase()));
+                    found = true;
+                }
+            }
+        }
+        assertTrue(found);
+
+        found = false;
+        for (EventCategoryTO eventCategoryTO : events) {
+            if (AttributableType.USER.name().toLowerCase().equals(eventCategoryTO.getCategory())) {
+                if (RESOURCE_NAME_CSV.equals(eventCategoryTO.getSubcategory())
+                        && EventCategoryType.PROPAGATION == eventCategoryTO.getType()) {
+                    assertTrue(eventCategoryTO.getEvents().contains(ResourceOperation.CREATE.name().toLowerCase()));
+                    assertTrue(eventCategoryTO.getEvents().contains(ResourceOperation.UPDATE.name().toLowerCase()));
+                    assertTrue(eventCategoryTO.getEvents().contains(ResourceOperation.DELETE.name().toLowerCase()));
+                    found = true;
+                }
+            }
+        }
+        assertTrue(found);
+
+        found = false;
+        for (EventCategoryTO eventCategoryTO : events) {
+            if (EventCategoryType.TASK == eventCategoryTO.getType()
+                    && "SampleJob".equals(eventCategoryTO.getCategory())) {
+                found = true;
+            }
+        }
+        assertTrue(found);
+
+        found = false;
+        for (EventCategoryTO eventCategoryTO : events) {
+            if (EventCategoryType.TASK == eventCategoryTO.getType()
+                    && "SyncJob".equals(eventCategoryTO.getCategory())) {
+                found = true;
+            }
+        }
+        assertTrue(found);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/NotificationITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/NotificationITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/NotificationITCase.java
new file mode 100644
index 0000000..cb09ea8
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/NotificationITCase.java
@@ -0,0 +1,172 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.NotificationTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.TraceLevel;
+import org.apache.syncope.common.rest.api.service.NotificationService;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class NotificationITCase extends AbstractITCase {
+
+    private NotificationTO buildNotificationTO() {
+        NotificationTO notificationTO = new NotificationTO();
+        notificationTO.setTraceLevel(TraceLevel.SUMMARY);
+        notificationTO.getEvents().add("create");
+
+        notificationTO.setUserAbout(SyncopeClient.getUserSearchConditionBuilder().
+                is("fullname").equalTo("*o*").and("fullname").equalTo("*i*").query());
+
+        notificationTO.setRecipientAttrName("email");
+        notificationTO.setRecipientAttrType(IntMappingType.UserPlainSchema);
+
+        notificationTO.setSender("syncope@syncope.apache.org");
+        notificationTO.setSubject("Test notification");
+        notificationTO.setTemplate("test");
+        return notificationTO;
+    }
+
+    @Test
+    public void read() {
+        NotificationTO notificationTO = notificationService.read(10L);
+        assertNotNull(notificationTO);
+    }
+
+    @Test
+    public void list() {
+        List<NotificationTO> notificationTOs = notificationService.list();
+        assertNotNull(notificationTOs);
+        assertFalse(notificationTOs.isEmpty());
+        for (NotificationTO instance : notificationTOs) {
+            assertNotNull(instance);
+        }
+    }
+
+    @Test
+    public void create() {
+        NotificationTO notificationTO = buildNotificationTO();
+        notificationTO.setRecipients(SyncopeClient.getUserSearchConditionBuilder().hasRoles(7L).query());
+
+        Response response = notificationService.create(notificationTO);
+        NotificationTO actual = getObject(response.getLocation(), NotificationService.class,
+                NotificationTO.class);
+
+        assertNotNull(actual);
+        assertNotNull(actual.getKey());
+        notificationTO.setKey(actual.getKey());
+        assertEquals(actual, notificationTO);
+    }
+
+    @Test
+    public void update() {
+        NotificationTO notificationTO = notificationService.read(10L);
+        notificationTO.setRecipients(SyncopeClient.getUserSearchConditionBuilder().hasRoles(7L).query());
+
+        notificationService.update(notificationTO.getKey(), notificationTO);
+        NotificationTO actual = notificationService.read(notificationTO.getKey());
+        assertNotNull(actual);
+        assertEquals(actual, notificationTO);
+    }
+
+    @Test
+    public void delete() {
+        NotificationTO notification = buildNotificationTO();
+        notification.setSelfAsRecipient(true);
+        Response response = notificationService.create(notification);
+        notification = getObject(response.getLocation(), NotificationService.class, NotificationTO.class);
+
+        notificationService.delete(notification.getKey());
+
+        try {
+            notificationService.read(notification.getKey());
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE83() {
+        NotificationTO notificationTO = buildNotificationTO();
+        notificationTO.setSelfAsRecipient(true);
+
+        NotificationTO actual = null;
+        try {
+            Response response = notificationService.create(notificationTO);
+            actual = getObject(response.getLocation(), NotificationService.class, NotificationTO.class);
+        } catch (SyncopeClientException e) {
+            assertNotNull(e);
+        }
+        assertNotNull(actual);
+        assertNotNull(actual.getKey());
+        notificationTO.setKey(actual.getKey());
+        assertEquals(actual, notificationTO);
+    }
+
+    @Test
+    public void issueSYNCOPE445() {
+        NotificationTO notificationTO = buildNotificationTO();
+        notificationTO.getStaticRecipients().add("syncope445@syncope.apache.org");
+
+        NotificationTO actual = null;
+        try {
+            Response response = notificationService.create(notificationTO);
+            actual = getObject(response.getLocation(), NotificationService.class, NotificationTO.class);
+        } catch (SyncopeClientException e) {
+            assertNotNull(e);
+        }
+        assertNotNull(actual);
+        assertNotNull(actual.getKey());
+        notificationTO.setKey(actual.getKey());
+        assertEquals(actual, notificationTO);
+    }
+
+    @Test
+    public void issueSYNCOPE446() {
+        NotificationTO notificationTO = buildNotificationTO();
+        notificationTO.getStaticRecipients().add("syncope446@syncope.apache.org");
+        notificationTO.setRoleAbout(SyncopeClient.getRoleSearchConditionBuilder().hasEntitlements("ROLE_READ").query());
+
+        NotificationTO actual = null;
+        try {
+            Response response = notificationService.create(notificationTO);
+            actual = getObject(response.getLocation(), NotificationService.class, NotificationTO.class);
+        } catch (SyncopeClientException e) {
+            assertNotNull(e);
+        }
+        assertNotNull(actual);
+        assertNotNull(actual.getKey());
+        notificationTO.setKey(actual.getKey());
+        assertEquals(actual, notificationTO);
+    }
+}


[03/15] syncope git commit: Merge branch 'master' into 2_0_X

Posted by il...@apache.org.
Merge branch 'master' into 2_0_X


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/b7d9addc
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/b7d9addc
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/b7d9addc

Branch: refs/heads/2_0_X
Commit: b7d9addc81adab2defe19e4fc4e6189a805aef2e
Parents: 961dfd3 336d8d6
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Wed Jan 21 06:56:10 2015 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Wed Jan 21 06:56:10 2015 +0100

----------------------------------------------------------------------
 .../core/rest/ConfigurationTestITCase.java      | 79 +++++++++++---------
 1 file changed, 42 insertions(+), 37 deletions(-)
----------------------------------------------------------------------



[11/15] syncope git commit: FIT server integration tests

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/SearchITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/SearchITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/SearchITCase.java
new file mode 100644
index 0000000..57dfbb0
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/SearchITCase.java
@@ -0,0 +1,182 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class SearchITCase extends AbstractITCase {
+
+    @Test
+    public void searchUser() {
+        // LIKE
+        PagedResult<UserTO> matchedUsers = userService.search(
+                SyncopeClient.getUserSearchConditionBuilder().
+                is("fullname").equalTo("*o*").and("fullname").equalTo("*i*").query());
+        assertNotNull(matchedUsers);
+        assertFalse(matchedUsers.getResult().isEmpty());
+
+        for (UserTO user : matchedUsers.getResult()) {
+            assertNotNull(user);
+        }
+
+        // ISNULL
+        matchedUsers = userService.search(
+                SyncopeClient.getUserSearchConditionBuilder().isNull("loginDate").query());
+        assertNotNull(matchedUsers);
+        assertFalse(matchedUsers.getResult().isEmpty());
+
+        Set<Long> userIds = new HashSet<Long>(matchedUsers.getResult().size());
+        for (UserTO user : matchedUsers.getResult()) {
+            userIds.add(user.getKey());
+        }
+        assertTrue(userIds.contains(2L));
+        assertTrue(userIds.contains(3L));
+    }
+
+    @Test
+    public void searchByUsernameAndKey() {
+        final PagedResult<UserTO> matchingUsers = userService.search(
+                SyncopeClient.getUserSearchConditionBuilder().
+                is("username").equalTo("rossini").and("key").lessThan(2).query());
+
+        assertNotNull(matchingUsers);
+        assertEquals(1, matchingUsers.getResult().size());
+        assertEquals("rossini", matchingUsers.getResult().iterator().next().getUsername());
+        assertEquals(1L, matchingUsers.getResult().iterator().next().getKey());
+    }
+
+    @Test
+    public void searchByRolenameAndKey() {
+        final PagedResult<RoleTO> matchingRoles = roleService.search(
+                SyncopeClient.getRoleSearchConditionBuilder().
+                is("name").equalTo("root").and("key").lessThan(2).query());
+
+        assertNotNull(matchingRoles);
+        assertEquals(1, matchingRoles.getResult().size());
+        assertEquals("root", matchingRoles.getResult().iterator().next().getName());
+        assertEquals(1L, matchingRoles.getResult().iterator().next().getKey());
+    }
+
+    @Test
+    public void searchUserByResourceName() {
+        PagedResult<UserTO> matchedUsers = userService.search(
+                SyncopeClient.getUserSearchConditionBuilder().hasResources(RESOURCE_NAME_MAPPINGS2).query());
+        assertNotNull(matchedUsers);
+        assertFalse(matchedUsers.getResult().isEmpty());
+
+        Set<Long> userIds = new HashSet<Long>(matchedUsers.getResult().size());
+        for (UserTO user : matchedUsers.getResult()) {
+            userIds.add(user.getKey());
+        }
+
+        assertEquals(1, userIds.size());
+        assertTrue(userIds.contains(2L));
+    }
+
+    @Test
+    public void paginatedSearch() {
+        // LIKE
+        PagedResult<UserTO> matchingUsers = userService.search(
+                SyncopeClient.getUserSearchConditionBuilder().
+                is("fullname").equalTo("*o*").and("fullname").equalTo("*i*").query(), 1, 2);
+        assertNotNull(matchingUsers);
+
+        assertFalse(matchingUsers.getResult().isEmpty());
+        for (UserTO user : matchingUsers.getResult()) {
+            assertNotNull(user);
+        }
+
+        // ISNULL
+        matchingUsers = userService.search(
+                SyncopeClient.getUserSearchConditionBuilder().isNull("loginDate").query(), 1, 2);
+
+        assertNotNull(matchingUsers);
+        assertFalse(matchingUsers.getResult().isEmpty());
+        Set<Long> userIds = new HashSet<>(matchingUsers.getResult().size());
+        for (UserTO user : matchingUsers.getResult()) {
+            userIds.add(user.getKey());
+        }
+        assertEquals(2, userIds.size());
+    }
+
+    @Test
+    public void searchByBooleanSubjectCond() {
+        final PagedResult<RoleTO> matchingRoles = roleService.search(
+                SyncopeClient.getRoleSearchConditionBuilder().is("inheritPlainAttrs").equalTo("true").query());
+        assertNotNull(matchingRoles);
+        assertFalse(matchingRoles.getResult().isEmpty());
+    }
+
+    @Test
+    public void searchByEntitlement() {
+        final PagedResult<RoleTO> matchingRoles = roleService.search(
+                SyncopeClient.getRoleSearchConditionBuilder().hasEntitlements("USER_LIST", "USER_READ").query());
+        assertNotNull(matchingRoles);
+        assertFalse(matchingRoles.getResult().isEmpty());
+    }
+
+    @Test
+    public void searchByRelationshipSubjectCond() {
+        final PagedResult<RoleTO> matchingRoles = roleService.search(SyncopeClient.getRoleSearchConditionBuilder().
+                isNotNull("passwordPolicy").and("userOwner").equalTo(5).query());
+
+        assertNotNull(matchingRoles);
+        assertEquals(1, matchingRoles.getResult().size());
+        assertEquals("director", matchingRoles.getResult().iterator().next().getName());
+        assertEquals(6L, matchingRoles.getResult().iterator().next().getKey());
+    }
+
+    @Test
+    public void nested() {
+        PagedResult<UserTO> matchedUsers = userService.search(
+                "((fullname==*o*,fullname==*i*);$resources!=ws-target-resource-1)", 1, 2);
+        assertNotNull(matchedUsers);
+
+        assertFalse(matchedUsers.getResult().isEmpty());
+        for (UserTO user : matchedUsers.getResult()) {
+            assertNotNull(user);
+        }
+    }
+
+    @Test
+    public void orderBy() {
+        final PagedResult<UserTO> matchedUsers = userService.search(
+                SyncopeClient.getUserSearchConditionBuilder().is("userId").equalTo("*@apache.org").query(),
+                SyncopeClient.getOrderByClauseBuilder().asc("status").desc("firstname").build());
+
+        assertFalse(matchedUsers.getResult().isEmpty());
+        for (UserTO user : matchedUsers.getResult()) {
+            assertNotNull(user);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/SecurityQuestionITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/SecurityQuestionITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/SecurityQuestionITCase.java
new file mode 100644
index 0000000..da65d29
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/SecurityQuestionITCase.java
@@ -0,0 +1,99 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.SecurityQuestionTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.rest.api.service.SecurityQuestionService;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class SecurityQuestionITCase extends AbstractITCase {
+
+    @Test
+    public void read() {
+        SecurityQuestionTO securityQuestionTO = securityQuestionService.read(1L);
+        assertNotNull(securityQuestionTO);
+    }
+
+    @Test
+    public void list() {
+        List<SecurityQuestionTO> securityQuestionTOs = securityQuestionService.list();
+        assertNotNull(securityQuestionTOs);
+        assertFalse(securityQuestionTOs.isEmpty());
+        for (SecurityQuestionTO instance : securityQuestionTOs) {
+            assertNotNull(instance);
+        }
+    }
+
+    @Test
+    public void create() {
+        SecurityQuestionTO securityQuestionTO = new SecurityQuestionTO();
+        securityQuestionTO.setContent("What is your favorite pet's name?");
+
+        Response response = securityQuestionService.create(securityQuestionTO);
+        SecurityQuestionTO actual = getObject(response.getLocation(), SecurityQuestionService.class,
+                SecurityQuestionTO.class);
+
+        assertNotNull(actual);
+        assertNotNull(actual.getKey());
+        securityQuestionTO.setKey(actual.getKey());
+        assertEquals(actual, securityQuestionTO);
+    }
+
+    @Test
+    public void update() {
+        SecurityQuestionTO securityQuestionTO = securityQuestionService.read(1L);
+        securityQuestionTO.setContent("What is your favorite color?");
+
+        securityQuestionService.update(securityQuestionTO.getKey(), securityQuestionTO);
+        SecurityQuestionTO actual = securityQuestionService.read(securityQuestionTO.getKey());
+        assertNotNull(actual);
+        assertEquals(actual, securityQuestionTO);
+    }
+
+    @Test
+    public void delete() {
+        SecurityQuestionTO securityQuestion = new SecurityQuestionTO();
+        securityQuestion.setContent("What is your first pet's name?");
+
+        Response response = securityQuestionService.create(securityQuestion);
+        securityQuestion = getObject(response.getLocation(), SecurityQuestionService.class, SecurityQuestionTO.class);
+
+        securityQuestionService.delete(securityQuestion.getKey());
+
+        try {
+            securityQuestionService.read(securityQuestion.getKey());
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/TaskITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/TaskITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/TaskITCase.java
new file mode 100644
index 0000000..c144dbd
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/TaskITCase.java
@@ -0,0 +1,1385 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+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.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.mod.StatusMod;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.AbstractTaskTO;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.BulkAction;
+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.NotificationTO;
+import org.apache.syncope.common.lib.to.NotificationTaskTO;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.PlainSchemaTO;
+import org.apache.syncope.common.lib.to.PropagationTaskTO;
+import org.apache.syncope.common.lib.to.PushTaskTO;
+import org.apache.syncope.common.lib.to.ReportExecTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.to.SchedTaskTO;
+import org.apache.syncope.common.lib.to.SyncPolicyTO;
+import org.apache.syncope.common.lib.to.SyncTaskTO;
+import org.apache.syncope.common.lib.to.TaskExecTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.CipherAlgorithm;
+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.MatchingRule;
+import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.syncope.common.lib.types.SubjectType;
+import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.common.lib.types.TraceLevel;
+import org.apache.syncope.common.lib.types.UnmatchingRule;
+import org.apache.syncope.common.lib.wrap.JobClass;
+import org.apache.syncope.common.lib.wrap.PushActionClass;
+import org.apache.syncope.common.lib.wrap.ResourceName;
+import org.apache.syncope.common.lib.wrap.SyncActionClass;
+import org.apache.syncope.common.rest.api.CollectionWrapper;
+import org.apache.syncope.common.rest.api.service.NotificationService;
+import org.apache.syncope.common.rest.api.service.ResourceService;
+import org.apache.syncope.common.rest.api.service.TaskService;
+import org.apache.syncope.server.misc.security.Encryptor;
+import org.apache.syncope.server.provisioning.api.job.SyncJob;
+import org.apache.syncope.server.provisioning.java.sync.DBPasswordSyncActions;
+import org.apache.syncope.server.provisioning.java.sync.LDAPPasswordSyncActions;
+import org.identityconnectors.framework.common.objects.Name;
+import org.junit.BeforeClass;
+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 TaskITCase extends AbstractITCase {
+
+    private static final Long SCHED_TASK_ID = 5L;
+
+    private static final Long SYNC_TASK_ID = 4L;
+
+    private static class ThreadExec implements Callable<TaskExecTO> {
+
+        private final TaskITCase test;
+
+        private final Long taskKey;
+
+        private final int maxWaitSeconds;
+
+        private final boolean dryRun;
+
+        public ThreadExec(TaskITCase test, Long taskKey, int maxWaitSeconds, boolean dryRun) {
+            this.test = test;
+            this.taskKey = taskKey;
+            this.maxWaitSeconds = maxWaitSeconds;
+            this.dryRun = dryRun;
+        }
+
+        @Override
+        public TaskExecTO call() throws Exception {
+            return test.execSyncTask(taskKey, maxWaitSeconds, dryRun);
+        }
+    }
+
+    @BeforeClass
+    public static void testSyncActionsSetup() {
+        SyncTaskTO syncTask = taskService.read(SYNC_TASK_ID);
+        syncTask.getActionsClassNames().add(TestSyncActions.class.getName());
+        taskService.update(SYNC_TASK_ID, syncTask);
+    }
+
+    /**
+     * Remove initial and synchronized users to make test re-runnable.
+     */
+    private void removeTestUsers() {
+        for (int i = 0; i < 10; i++) {
+            String cUserName = "test" + i;
+            try {
+                UserTO cUserTO = readUser(cUserName);
+                userService.delete(cUserTO.getKey());
+            } catch (Exception e) {
+                // Ignore
+            }
+        }
+    }
+
+    @Test
+    public void getJobClasses() {
+        List<JobClass> jobClasses = taskService.getJobClasses();
+        assertNotNull(jobClasses);
+        assertFalse(jobClasses.isEmpty());
+    }
+
+    @Test
+    public void getSyncActionsClasses() {
+        List<SyncActionClass> actions = taskService.getSyncActionsClasses();
+        assertNotNull(actions);
+        assertFalse(actions.isEmpty());
+    }
+
+    @Test
+    public void getPushActionsClasses() {
+        List<PushActionClass> actions = taskService.getPushActionsClasses();
+        assertNotNull(actions);
+    }
+
+    @Test
+    public void createSyncTask() {
+        SyncTaskTO task = new SyncTaskTO();
+        task.setName("Test create Sync");
+        task.setResource(RESOURCE_NAME_WS2);
+
+        UserTO userTemplate = new UserTO();
+        userTemplate.getResources().add(RESOURCE_NAME_WS2);
+
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(8L);
+        userTemplate.getMemberships().add(membershipTO);
+        task.setUserTemplate(userTemplate);
+
+        RoleTO roleTemplate = new RoleTO();
+        roleTemplate.getResources().add(RESOURCE_NAME_LDAP);
+        task.setRoleTemplate(roleTemplate);
+
+        Response response = taskService.create(task);
+        SyncTaskTO actual = getObject(response.getLocation(), TaskService.class, SyncTaskTO.class);
+        assertNotNull(actual);
+
+        task = taskService.read(actual.getKey());
+        assertNotNull(task);
+        assertEquals(actual.getKey(), task.getKey());
+        assertEquals(actual.getJobClassName(), task.getJobClassName());
+        assertEquals(userTemplate, task.getUserTemplate());
+        assertEquals(roleTemplate, task.getRoleTemplate());
+    }
+
+    @Test
+    public void createPushTask() {
+        PushTaskTO task = new PushTaskTO();
+        task.setName("Test create Push");
+        task.setResource(RESOURCE_NAME_WS2);
+        task.setUserFilter(
+                SyncopeClient.getUserSearchConditionBuilder().hasNotResources(RESOURCE_NAME_TESTDB2).query());
+        task.setRoleFilter(
+                SyncopeClient.getRoleSearchConditionBuilder().isNotNull("cool").query());
+        task.setMatchingRule(MatchingRule.LINK);
+
+        final Response response = taskService.create(task);
+        final PushTaskTO actual = getObject(response.getLocation(), TaskService.class, PushTaskTO.class);
+        assertNotNull(actual);
+
+        task = taskService.read(actual.getKey());
+        assertNotNull(task);
+        assertEquals(task.getKey(), actual.getKey());
+        assertEquals(task.getJobClassName(), actual.getJobClassName());
+        assertEquals(task.getUserFilter(), actual.getUserFilter());
+        assertEquals(task.getRoleFilter(), actual.getRoleFilter());
+        assertEquals(UnmatchingRule.ASSIGN, actual.getUnmatchingRule());
+        assertEquals(MatchingRule.LINK, actual.getMatchingRule());
+    }
+
+    @Test
+    public void update() {
+        SchedTaskTO task = taskService.read(SCHED_TASK_ID);
+        assertNotNull(task);
+
+        final SchedTaskTO taskMod = new SchedTaskTO();
+        taskMod.setKey(5);
+        taskMod.setCronExpression(null);
+
+        taskService.update(taskMod.getKey(), taskMod);
+        SchedTaskTO actual = taskService.read(taskMod.getKey());
+        assertNotNull(actual);
+        assertEquals(task.getKey(), actual.getKey());
+        assertNull(actual.getCronExpression());
+    }
+
+    @Test
+    public void listSchedTask() {
+        final PagedResult<SchedTaskTO> tasks = taskService.list(TaskType.SCHEDULED);
+        assertFalse(tasks.getResult().isEmpty());
+        for (AbstractTaskTO task : tasks.getResult()) {
+            if (!(task instanceof SchedTaskTO) || task instanceof SyncTaskTO || task instanceof PushTaskTO) {
+                fail();
+            }
+        }
+    }
+
+    @Test
+    public void listSyncTask() {
+        final PagedResult<SyncTaskTO> tasks = taskService.list(TaskType.SYNCHRONIZATION);
+        assertFalse(tasks.getResult().isEmpty());
+        for (AbstractTaskTO task : tasks.getResult()) {
+            if (!(task instanceof SyncTaskTO)) {
+                fail();
+            }
+        }
+    }
+
+    @Test
+    public void list() {
+        final PagedResult<PushTaskTO> tasks = taskService.list(TaskType.PUSH);
+        assertFalse(tasks.getResult().isEmpty());
+        for (AbstractTaskTO task : tasks.getResult()) {
+            if (!(task instanceof PushTaskTO)) {
+                fail();
+            }
+        }
+    }
+
+    @Test
+    public void paginatedList() {
+        PagedResult<PropagationTaskTO> tasks = taskService.list(TaskType.PROPAGATION, 1, 2);
+
+        assertNotNull(tasks);
+        assertFalse(tasks.getResult().isEmpty());
+        assertEquals(2, tasks.getResult().size());
+
+        for (AbstractTaskTO task : tasks.getResult()) {
+            assertNotNull(task);
+        }
+
+        tasks = taskService.list(TaskType.PROPAGATION, 2, 2);
+
+        assertNotNull(tasks);
+        assertFalse(tasks.getResult().isEmpty());
+
+        for (AbstractTaskTO task : tasks.getResult()) {
+            assertNotNull(task);
+        }
+
+        tasks = taskService.list(TaskType.PROPAGATION, 1000, 2);
+
+        assertNotNull(tasks);
+        assertTrue(tasks.getResult().isEmpty());
+    }
+
+    @Test
+    public void read() {
+        final PropagationTaskTO taskTO = taskService.read(3L);
+
+        assertNotNull(taskTO);
+        assertNotNull(taskTO.getExecutions());
+        assertTrue(taskTO.getExecutions().isEmpty());
+
+        final PushTaskTO pushTaskTO = taskService.<PushTaskTO>read(17L);
+        assertEquals(UnmatchingRule.ASSIGN, pushTaskTO.getUnmatchingRule());
+        assertEquals(MatchingRule.UPDATE, pushTaskTO.getMatchingRule());
+    }
+
+    @Test
+    public void readExecution() {
+        TaskExecTO taskTO = taskService.readExecution(6L);
+        assertNotNull(taskTO);
+    }
+
+    @Test
+    // Currently test is not re-runnable.
+    // To successfully run test second time it is necessary to restart cargo.
+    public void deal() {
+        try {
+            taskService.delete(0L);
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
+        }
+        TaskExecTO exec = taskService.execute(1L, false);
+        assertEquals(PropagationTaskExecStatus.SUBMITTED.name(), exec.getStatus());
+
+        ReportExecTO report = new ReportExecTO();
+        report.setStatus(PropagationTaskExecStatus.SUCCESS.name());
+        report.setMessage("OK");
+        taskService.report(exec.getKey(), report);
+        exec = taskService.readExecution(exec.getKey());
+        assertEquals(PropagationTaskExecStatus.SUCCESS.name(), exec.getStatus());
+        assertEquals("OK", exec.getMessage());
+
+        taskService.delete(1L);
+        try {
+            taskService.readExecution(exec.getKey());
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
+        }
+    }
+
+    @Test
+    public void sync() throws Exception {
+        removeTestUsers();
+
+        // -----------------------------
+        // Create a new user ... it should be updated applying sync policy
+        // -----------------------------
+        UserTO inUserTO = new UserTO();
+        inUserTO.setPassword("password123");
+        String userName = "test9";
+        inUserTO.setUsername(userName);
+        inUserTO.getPlainAttrs().add(attrTO("firstname", "nome9"));
+        inUserTO.getPlainAttrs().add(attrTO("surname", "cognome"));
+        inUserTO.getPlainAttrs().add(attrTO("type", "a type"));
+        inUserTO.getPlainAttrs().add(attrTO("fullname", "nome cognome"));
+        inUserTO.getPlainAttrs().add(attrTO("userId", "puccini@syncope.apache.org"));
+        inUserTO.getPlainAttrs().add(attrTO("email", "puccini@syncope.apache.org"));
+        inUserTO.getDerAttrs().add(attrTO("csvuserid", null));
+
+        inUserTO = createUser(inUserTO);
+        assertNotNull(inUserTO);
+        assertFalse(inUserTO.getResources().contains(RESOURCE_NAME_CSV));
+
+        // -----------------------------
+        try {
+            int usersPre = userService.list(1, 1).getTotalCount();
+            assertNotNull(usersPre);
+
+            execSyncTask(SYNC_TASK_ID, 50, false);
+
+            // after execution of the sync task the user data should be synced from
+            // csv datasource and processed by user template
+            UserTO userTO = userService.read(inUserTO.getKey());
+            assertNotNull(userTO);
+            assertEquals(userName, userTO.getUsername());
+            assertEquals(ActivitiDetector.isActivitiEnabledForUsers() ? "active" : "created", userTO.getStatus());
+            assertEquals("test9@syncope.apache.org", userTO.getPlainAttrMap().get("email").getValues().get(0));
+            assertEquals("test9@syncope.apache.org", userTO.getPlainAttrMap().get("userId").getValues().get(0));
+            assertTrue(Integer.valueOf(userTO.getPlainAttrMap().get("fullname").getValues().get(0)) <= 10);
+            assertTrue(userTO.getResources().contains(RESOURCE_NAME_TESTDB));
+            assertTrue(userTO.getResources().contains(RESOURCE_NAME_WS2));
+
+            // Matching --> Update (no link)
+            assertFalse(userTO.getResources().contains(RESOURCE_NAME_CSV));
+
+            // check for user template
+            userTO = readUser("test7");
+            assertNotNull(userTO);
+            assertEquals("TYPE_OTHER", userTO.getPlainAttrMap().get("type").getValues().get(0));
+            assertEquals(3, userTO.getResources().size());
+            assertTrue(userTO.getResources().contains(RESOURCE_NAME_TESTDB));
+            assertTrue(userTO.getResources().contains(RESOURCE_NAME_WS2));
+            assertEquals(1, userTO.getMemberships().size());
+            assertTrue(userTO.getMemberships().get(0).getPlainAttrMap().containsKey("subscriptionDate"));
+
+            // Unmatching --> Assign (link)
+            assertTrue(userTO.getResources().contains(RESOURCE_NAME_CSV));
+
+            userTO = readUser("test8");
+            assertNotNull(userTO);
+            assertEquals("TYPE_8", userTO.getPlainAttrMap().get("type").getValues().get(0));
+
+            // check for sync results
+            int usersPost = userService.list(1, 1).getTotalCount();
+            assertNotNull(usersPost);
+            assertEquals(usersPre + 9, usersPost);
+
+            // Check for issue 215:
+            // * expected disabled user test1
+            // * expected enabled user test2
+            userTO = readUser("test1");
+            assertNotNull(userTO);
+            assertEquals("suspended", userTO.getStatus());
+
+            userTO = readUser("test3");
+            assertNotNull(userTO);
+            assertEquals("active", userTO.getStatus());
+
+            // SYNCOPE-317
+            execSyncTask(SYNC_TASK_ID, 50, false);
+
+            final Set<Long> pushTaskIds = new HashSet<Long>();
+            pushTaskIds.add(25L);
+            pushTaskIds.add(26L);
+
+            execSyncTasks(pushTaskIds, 50, false);
+            // Matching --> UNLINK
+            assertFalse(readUser("test9").getResources().contains(RESOURCE_NAME_CSV));
+            assertFalse(readUser("test7").getResources().contains(RESOURCE_NAME_CSV));
+        } finally {
+            removeTestUsers();
+        }
+    }
+
+    @Test
+    public void reconcileFromDB() {
+        // update sync task
+        TaskExecTO execution = execSyncTask(7L, 50, false);
+        assertNotNull(execution.getStatus());
+        assertTrue(PropagationTaskExecStatus.valueOf(execution.getStatus()).isSuccessful());
+
+        UserTO userTO = readUser("testuser1");
+        assertNotNull(userTO);
+        assertEquals("reconciled@syncope.apache.org", userTO.getPlainAttrMap().get("userId").getValues().get(0));
+        assertEquals("suspended", userTO.getStatus());
+
+        // enable user on external resource
+        JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+        jdbcTemplate.execute("UPDATE TEST SET STATUS=TRUE");
+
+        // re-execute the same SyncTask: now user must be active
+        execution = execSyncTask(7L, 50, false);
+        assertNotNull(execution.getStatus());
+        assertTrue(PropagationTaskExecStatus.valueOf(execution.getStatus()).isSuccessful());
+
+        userTO = readUser("testuser1");
+        assertNotNull(userTO);
+        assertEquals("active", userTO.getStatus());
+    }
+
+    /**
+     * Clean Syncope and LDAP resource status.
+     */
+    private void ldapCleanup() {
+        PagedResult<RoleTO> matchingRoles = roleService.search(
+                SyncopeClient.getRoleSearchConditionBuilder().is("name").equalTo("testLDAPGroup").query());
+        if (matchingRoles.getSize() > 0) {
+            for (RoleTO role : matchingRoles.getResult()) {
+                roleService.bulkDeassociation(role.getKey(),
+                        ResourceDeassociationActionType.UNLINK,
+                        CollectionWrapper.wrap(RESOURCE_NAME_LDAP, ResourceName.class));
+                roleService.delete(role.getKey());
+            }
+        }
+        PagedResult<UserTO> matchingUsers = userService.search(
+                SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("syncFromLDAP").query());
+        if (matchingUsers.getSize() > 0) {
+            for (UserTO user : matchingUsers.getResult()) {
+                userService.bulkDeassociation(user.getKey(),
+                        ResourceDeassociationActionType.UNLINK,
+                        CollectionWrapper.wrap(RESOURCE_NAME_LDAP, ResourceName.class));
+                userService.delete(user.getKey());
+            }
+        }
+    }
+
+    @Test
+    public void reconcileFromLDAP() {
+        // First of all, clear any potential conflict with existing user / role
+        ldapCleanup();
+
+        // Update sync task
+        TaskExecTO execution = execSyncTask(11L, 50, false);
+
+        // 1. verify execution status
+        final String status = execution.getStatus();
+        assertNotNull(status);
+        assertTrue(PropagationTaskExecStatus.valueOf(status).isSuccessful());
+
+        // 2. verify that synchronized role is found, with expected attributes
+        final PagedResult<RoleTO> matchingRoles = roleService.search(
+                SyncopeClient.getRoleSearchConditionBuilder().is("name").equalTo("testLDAPGroup").query());
+        assertNotNull(matchingRoles);
+        assertEquals(1, matchingRoles.getResult().size());
+
+        final PagedResult<UserTO> matchingUsers = userService.search(
+                SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("syncFromLDAP").query());
+        assertNotNull(matchingUsers);
+        assertEquals(1, matchingUsers.getResult().size());
+
+        // Check for SYNCOPE-436
+        assertEquals("syncFromLDAP",
+                matchingUsers.getResult().get(0).getVirAttrMap().get("virtualReadOnly").getValues().get(0));
+        // Check for SYNCOPE-270
+        assertNotNull(matchingUsers.getResult().get(0).getPlainAttrMap().get("obscure"));
+        // Check for SYNCOPE-123
+        assertNotNull(matchingUsers.getResult().get(0).getPlainAttrMap().get("photo"));
+
+        final RoleTO roleTO = matchingRoles.getResult().iterator().next();
+        assertNotNull(roleTO);
+        assertEquals("testLDAPGroup", roleTO.getName());
+        assertEquals(8L, roleTO.getParent());
+        assertEquals("true", roleTO.getPlainAttrMap().get("show").getValues().get(0));
+        assertEquals(matchingUsers.getResult().iterator().next().getKey(), (long) roleTO.getUserOwner());
+        assertNull(roleTO.getRoleOwner());
+
+        // 3. verify that LDAP group membership is propagated as Syncope role membership
+        final PagedResult<UserTO> members = userService.search(
+                SyncopeClient.getUserSearchConditionBuilder().hasRoles(roleTO.getKey()).query());
+        assertNotNull(members);
+        assertEquals(1, members.getResult().size());
+    }
+
+    @Test
+    public void issue196() {
+        TaskExecTO exec = taskService.execute(6L, false);
+        assertNotNull(exec);
+        assertEquals(0, exec.getKey());
+        assertNotNull(exec.getTask());
+    }
+
+    @Test
+    public void dryRun() {
+        TaskExecTO execution = execSyncTask(SYNC_TASK_ID, 50, true);
+        assertEquals("Execution of task " + execution.getTask() + " failed with message " + execution.getMessage(),
+                "SUCCESS", execution.getStatus());
+    }
+
+    @Test
+    public void issueSYNCOPE81() {
+        String sender = "syncope81@syncope.apache.org";
+        createNotificationTask(sender);
+        NotificationTaskTO taskTO = findNotificationTaskBySender(sender);
+        assertNotNull(taskTO);
+
+        assertTrue(taskTO.getExecutions().isEmpty());
+
+        // generate an execution in order to verify the deletion of a notification task with one or more executions
+        TaskExecTO execution = taskService.execute(taskTO.getKey(), false);
+        assertEquals("NOT_SENT", execution.getStatus());
+
+        int i = 0;
+        int maxit = 50;
+        int executions = 0;
+
+        // wait for task exec completion (executions incremented)
+        do {
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+            }
+
+            taskTO = taskService.read(taskTO.getKey());
+
+            assertNotNull(taskTO);
+            assertNotNull(taskTO.getExecutions());
+
+            i++;
+        } while (executions == taskTO.getExecutions().size() && i < maxit);
+
+        assertFalse(taskTO.getExecutions().isEmpty());
+
+        taskService.delete(taskTO.getKey());
+    }
+
+    @Test
+    public void issueSYNCOPE86() {
+        // 1. create notification task
+        String sender = "syncope86@syncope.apache.org";
+        createNotificationTask(sender);
+
+        // 2. get NotificationTaskTO for user just created
+        NotificationTaskTO taskTO = findNotificationTaskBySender(sender);
+        assertNotNull(taskTO);
+        assertTrue(taskTO.getExecutions().isEmpty());
+
+        try {
+            // 3. execute the generated NotificationTask
+            TaskExecTO execution = taskService.execute(taskTO.getKey(), false);
+            assertNotNull(execution);
+
+            // 4. verify
+            taskTO = taskService.read(taskTO.getKey());
+            assertNotNull(taskTO);
+            assertEquals(1, taskTO.getExecutions().size());
+        } finally {
+            // Remove execution to make test re-runnable
+            taskService.deleteExecution(taskTO.getExecutions().get(0).getKey());
+        }
+    }
+
+    private NotificationTaskTO findNotificationTaskBySender(final String sender) {
+        PagedResult<NotificationTaskTO> tasks = taskService.list(TaskType.NOTIFICATION);
+        assertNotNull(tasks);
+        assertFalse(tasks.getResult().isEmpty());
+        NotificationTaskTO taskTO = null;
+        for (NotificationTaskTO task : tasks.getResult()) {
+            if (sender.equals(task.getSender())) {
+                taskTO = task;
+            }
+        }
+        return taskTO;
+    }
+
+    private void createNotificationTask(final String sender) {
+        // 1. Create notification
+        NotificationTO notification = new NotificationTO();
+        notification.setTraceLevel(TraceLevel.FAILURES);
+        notification.getEvents().add("[REST]:[UserLogic]:[]:[create]:[SUCCESS]");
+
+        notification.setUserAbout(SyncopeClient.getUserSearchConditionBuilder().hasRoles(7L).query());
+
+        notification.setRecipients(SyncopeClient.getUserSearchConditionBuilder().hasRoles(8L).query());
+        notification.setSelfAsRecipient(true);
+
+        notification.setRecipientAttrName("email");
+        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
+
+        notification.setSender(sender);
+        String subject = "Test notification";
+        notification.setSubject(subject);
+        notification.setTemplate("optin");
+        notification.setActive(true);
+
+        Response response = notificationService.create(notification);
+        notification = getObject(response.getLocation(), NotificationService.class, NotificationTO.class);
+        assertNotNull(notification);
+
+        // 2. create user
+        UserTO userTO = UserITCase.getUniqueSampleTO("syncope@syncope.apache.org");
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(7);
+        userTO.getMemberships().add(membershipTO);
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+    }
+
+    @Test
+    public void issueSYNCOPE68() {
+        //-----------------------------
+        // Create a new user ... it should be updated applying sync policy
+        //-----------------------------
+        UserTO userTO = new UserTO();
+        userTO.setPassword("password123");
+        userTO.setUsername("testuser2");
+
+        userTO.getPlainAttrs().add(attrTO("firstname", "testuser2"));
+        userTO.getPlainAttrs().add(attrTO("surname", "testuser2"));
+        userTO.getPlainAttrs().add(attrTO("type", "a type"));
+        userTO.getPlainAttrs().add(attrTO("fullname", "a type"));
+        userTO.getPlainAttrs().add(attrTO("userId", "testuser2@syncope.apache.org"));
+        userTO.getPlainAttrs().add(attrTO("email", "testuser2@syncope.apache.org"));
+
+        userTO.getResources().add(RESOURCE_NAME_NOPROPAGATION2);
+        userTO.getResources().add(RESOURCE_NAME_NOPROPAGATION4);
+
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(7L);
+
+        userTO.getMemberships().add(membershipTO);
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+        assertEquals("testuser2", userTO.getUsername());
+        assertEquals(1, userTO.getMemberships().size());
+        assertEquals(3, userTO.getResources().size());
+        //-----------------------------
+
+        try {
+            //-----------------------------
+            //  add user template
+            //-----------------------------
+            UserTO template = new UserTO();
+
+            membershipTO = new MembershipTO();
+            membershipTO.setRoleId(10L);
+
+            template.getMemberships().add(membershipTO);
+
+            template.getResources().add(RESOURCE_NAME_NOPROPAGATION4);
+            //-----------------------------
+
+            // Update sync task
+            SyncTaskTO task = taskService.read(9L);
+            assertNotNull(task);
+
+            task.setUserTemplate(template);
+
+            taskService.update(task.getKey(), task);
+            SyncTaskTO actual = taskService.read(task.getKey());
+            assertNotNull(actual);
+            assertEquals(task.getKey(), actual.getKey());
+            assertFalse(actual.getUserTemplate().getResources().isEmpty());
+            assertFalse(actual.getUserTemplate().getMemberships().isEmpty());
+
+            TaskExecTO execution = execSyncTask(actual.getKey(), 50, false);
+            final String status = execution.getStatus();
+            assertNotNull(status);
+            assertTrue(PropagationTaskExecStatus.valueOf(status).isSuccessful());
+
+            userTO = readUser("testuser2");
+            assertNotNull(userTO);
+            assertEquals("testuser2@syncope.apache.org", userTO.getPlainAttrMap().get("userId").getValues().get(0));
+            assertEquals(2, userTO.getMemberships().size());
+            assertEquals(4, userTO.getResources().size());
+        } finally {
+            UserTO dUserTO = deleteUser(userTO.getKey());
+            assertNotNull(dUserTO);
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE144() {
+        SchedTaskTO task = new SchedTaskTO();
+        task.setName("issueSYNCOPE144");
+        task.setDescription("issueSYNCOPE144 Description");
+        task.setJobClassName(SyncJob.class.getName());
+
+        Response response = taskService.create(task);
+        SchedTaskTO actual = getObject(response.getLocation(), TaskService.class, SchedTaskTO.class);
+        assertNotNull(actual);
+        assertEquals("issueSYNCOPE144", actual.getName());
+        assertEquals("issueSYNCOPE144 Description", actual.getDescription());
+
+        task = taskService.read(actual.getKey());
+        assertNotNull(task);
+        assertEquals("issueSYNCOPE144", task.getName());
+        assertEquals("issueSYNCOPE144 Description", task.getDescription());
+
+        task.setName("issueSYNCOPE144_2");
+        task.setDescription("issueSYNCOPE144 Description_2");
+
+        response = taskService.create(task);
+        actual = getObject(response.getLocation(), TaskService.class, SchedTaskTO.class);
+        assertNotNull(actual);
+        assertEquals("issueSYNCOPE144_2", actual.getName());
+        assertEquals("issueSYNCOPE144 Description_2", actual.getDescription());
+    }
+
+    @Test
+    public void issueSYNCOPE230() {
+        // 1. read SyncTask for resource-db-sync (table TESTSYNC on external H2)
+        execSyncTask(10L, 50, false);
+
+        // 3. read e-mail address for user created by the SyncTask first execution
+        UserTO userTO = readUser("issuesyncope230");
+        assertNotNull(userTO);
+        String email = userTO.getPlainAttrMap().get("email").getValues().iterator().next();
+        assertNotNull(email);
+
+        // 4. update TESTSYNC on external H2 by changing e-mail address
+        JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+        jdbcTemplate.execute("UPDATE TESTSYNC SET email='updatedSYNCOPE230@syncope.apache.org'");
+
+        // 5. re-execute the SyncTask
+        execSyncTask(10L, 50, false);
+
+        // 6. verify that the e-mail was updated
+        userTO = readUser("issuesyncope230");
+        assertNotNull(userTO);
+        email = userTO.getPlainAttrMap().get("email").getValues().iterator().next();
+        assertNotNull(email);
+        assertEquals("updatedSYNCOPE230@syncope.apache.org", email);
+    }
+
+    private TaskExecTO execSyncTask(final Long taskKey, final int maxWaitSeconds, final boolean dryRun) {
+        AbstractTaskTO taskTO = taskService.read(taskKey);
+        assertNotNull(taskTO);
+        assertNotNull(taskTO.getExecutions());
+
+        int preSyncSize = taskTO.getExecutions().size();
+        TaskExecTO execution = taskService.execute(taskTO.getKey(), dryRun);
+        assertEquals("JOB_FIRED", execution.getStatus());
+
+        int i = 0;
+        int maxit = maxWaitSeconds;
+
+        // wait for sync completion (executions incremented)
+        do {
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+            }
+
+            taskTO = taskService.read(taskTO.getKey());
+
+            assertNotNull(taskTO);
+            assertNotNull(taskTO.getExecutions());
+
+            i++;
+        } while (preSyncSize == taskTO.getExecutions().size() && i < maxit);
+        if (i == maxit) {
+            fail("Timeout when executing task " + taskKey);
+        }
+        return taskTO.getExecutions().get(taskTO.getExecutions().size() - 1);
+    }
+
+    private Map<Long, TaskExecTO> execSyncTasks(
+            final Set<Long> taskKeys, final int maxWaitSeconds, final boolean dryRun) throws Exception {
+
+        final ExecutorService service = Executors.newFixedThreadPool(taskKeys.size());
+        final List<Future<TaskExecTO>> futures = new ArrayList<>();
+
+        for (final Long id : taskKeys) {
+            futures.add(service.submit(new ThreadExec(this, id, maxWaitSeconds, dryRun)));
+        }
+
+        final Map<Long, TaskExecTO> res = new HashMap<>();
+
+        for (Future<TaskExecTO> f : futures) {
+            TaskExecTO taskExecTO = f.get(100, TimeUnit.SECONDS);
+            res.put(taskExecTO.getTask(), taskExecTO);
+        }
+
+        service.shutdownNow();
+
+        return res;
+    }
+
+    @Test
+    public void issueSYNCOPE272() {
+        removeTestUsers();
+
+        // create user with testdb resource
+        UserTO userTO = UserITCase.getUniqueSampleTO("syncope272@syncope.apache.org");
+        userTO.getResources().add(RESOURCE_NAME_TESTDB);
+
+        userTO = createUser(userTO);
+        try {
+            assertNotNull(userTO);
+            assertEquals(1, userTO.getPropagationStatusTOs().size());
+            assertTrue(userTO.getPropagationStatusTOs().get(0).getStatus().isSuccessful());
+
+            TaskExecTO taskExecTO = execSyncTask(24L, 50, false);
+
+            assertNotNull(taskExecTO.getStatus());
+            assertTrue(PropagationTaskExecStatus.valueOf(taskExecTO.getStatus()).isSuccessful());
+
+            userTO = userService.read(userTO.getKey());
+            assertNotNull(userTO);
+            assertNotNull(userTO.getPlainAttrMap().get("firstname").getValues().get(0));
+        } finally {
+            removeTestUsers();
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE258() {
+        // -----------------------------
+        // Add a custom correlation rule
+        // -----------------------------
+        SyncPolicyTO policyTO = policyService.read(9L);
+        policyTO.getSpecification().setUserJavaRule(TestSyncRule.class.getName());
+
+        policyService.update(policyTO.getKey(), policyTO);
+        // -----------------------------
+
+        SyncTaskTO task = new SyncTaskTO();
+        task.setName("Test Sync Rule");
+        task.setResource(RESOURCE_NAME_WS2);
+        task.setFullReconciliation(true);
+        task.setPerformCreate(true);
+        task.setPerformDelete(true);
+        task.setPerformUpdate(true);
+
+        Response response = taskService.create(task);
+        SyncTaskTO actual = getObject(response.getLocation(), TaskService.class, SyncTaskTO.class);
+        assertNotNull(actual);
+
+        UserTO userTO = UserITCase.getUniqueSampleTO("s258_1@apache.org");
+        userTO.getResources().clear();
+        userTO.getResources().add(RESOURCE_NAME_WS2);
+
+        createUser(userTO);
+
+        userTO = UserITCase.getUniqueSampleTO("s258_2@apache.org");
+        userTO.getResources().clear();
+        userTO.getResources().add(RESOURCE_NAME_WS2);
+
+        userTO = createUser(userTO);
+
+        // change email in order to unmatch the second user
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.getPlainAttrsToRemove().add("email");
+        userMod.getPlainAttrsToUpdate().add(attrMod("email", "s258@apache.org"));
+
+        userService.update(userMod.getKey(), userMod);
+
+        execSyncTask(actual.getKey(), 50, false);
+
+        SyncTaskTO executed = taskService.read(actual.getKey());
+        assertEquals(1, executed.getExecutions().size());
+
+        // asser for just one match
+        assertTrue(executed.getExecutions().get(0).getMessage().substring(0, 55) + "...",
+                executed.getExecutions().get(0).getMessage().contains("[updated/failures]: 1/0"));
+    }
+
+    @Test
+    public void issueSYNCOPE307() {
+        UserTO userTO = UserITCase.getUniqueSampleTO("s307@apache.org");
+
+        AttrTO csvuserid = new AttrTO();
+        csvuserid.setSchema("csvuserid");
+        userTO.getDerAttrs().add(csvuserid);
+
+        userTO.getResources().clear();
+        userTO.getResources().add(RESOURCE_NAME_WS2);
+        userTO.getResources().add(RESOURCE_NAME_CSV);
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        userTO = userService.read(userTO.getKey());
+        assertEquals("virtualvalue", userTO.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+        // Update sync task
+        SyncTaskTO task = taskService.read(12L);
+        assertNotNull(task);
+
+        //  add user template
+        UserTO template = new UserTO();
+        template.getResources().add(RESOURCE_NAME_DBVIRATTR);
+
+        AttrTO userId = attrTO("userId", "'s307@apache.org'");
+        template.getPlainAttrs().add(userId);
+
+        AttrTO email = attrTO("email", "'s307@apache.org'");
+        template.getPlainAttrs().add(email);
+
+        task.setUserTemplate(template);
+
+        taskService.update(task.getKey(), task);
+        execSyncTask(task.getKey(), 50, false);
+
+        // check for sync policy
+        userTO = userService.read(userTO.getKey());
+        assertEquals("virtualvalue", userTO.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+        try {
+            final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+
+            String value = jdbcTemplate.queryForObject(
+                    "SELECT USERNAME FROM testsync WHERE ID=?", String.class, userTO.getKey());
+            assertEquals("virtualvalue", value);
+        } catch (EmptyResultDataAccessException e) {
+            assertTrue(false);
+        }
+    }
+
+    @Test
+    public void bulkAction() {
+        final PagedResult<PropagationTaskTO> before = taskService.list(TaskType.PROPAGATION);
+
+        // create user with testdb resource
+        final UserTO userTO = UserITCase.getUniqueSampleTO("taskBulk@apache.org");
+        userTO.getResources().add(RESOURCE_NAME_TESTDB);
+        createUser(userTO);
+
+        final List<PropagationTaskTO> after = new ArrayList<PropagationTaskTO>(
+                taskService.<PropagationTaskTO>list(TaskType.PROPAGATION).getResult());
+
+        after.removeAll(before.getResult());
+
+        assertFalse(after.isEmpty());
+
+        final BulkAction bulkAction = new BulkAction();
+        bulkAction.setOperation(BulkAction.Type.DELETE);
+
+        for (AbstractTaskTO taskTO : after) {
+            bulkAction.getTargets().add(String.valueOf(taskTO.getKey()));
+        }
+
+        taskService.bulk(bulkAction);
+
+        assertFalse(taskService.list(TaskType.PROPAGATION).getResult().containsAll(after));
+    }
+
+    @Test
+    public void pushMatchingUnmatchingRoles() {
+        assertFalse(roleService.read(3L).getResources().contains(RESOURCE_NAME_LDAP));
+
+        execSyncTask(23L, 50, false);
+
+        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, 3L));
+        assertTrue(roleService.read(3L).getResources().contains(RESOURCE_NAME_LDAP));
+
+        execSyncTask(23L, 50, false);
+
+        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, 3L));
+        assertFalse(roleService.read(3L).getResources().contains(RESOURCE_NAME_LDAP));
+    }
+
+    @Test
+    public void pushUnmatchingUsers() throws Exception {
+        assertFalse(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
+        assertFalse(userService.read(3L).getResources().contains(RESOURCE_NAME_TESTDB2));
+        assertFalse(userService.read(4L).getResources().contains(RESOURCE_NAME_TESTDB2));
+        assertTrue(userService.read(5L).getResources().contains(RESOURCE_NAME_TESTDB2));
+
+        final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+        assertEquals(0, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='puccini'").size());
+
+        // ------------------------------------------
+        // Unmatching --> Assign --> dryRuyn
+        // ------------------------------------------
+        execSyncTask(13L, 50, true);
+        assertEquals(0, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='vivaldi'").size());
+        assertFalse(userService.read(3L).getResources().contains(RESOURCE_NAME_TESTDB2));
+        // ------------------------------------------
+
+        final Set<Long> pushTaskIds = new HashSet<>();
+        pushTaskIds.add(13L);
+        pushTaskIds.add(14L);
+        pushTaskIds.add(15L);
+        pushTaskIds.add(16L);
+        execSyncTasks(pushTaskIds, 50, false);
+
+        // ------------------------------------------
+        // Unatching --> Ignore
+        // ------------------------------------------
+        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='verdi'").size());
+        assertFalse(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
+        // ------------------------------------------
+
+        // ------------------------------------------
+        // Unmatching --> Assign
+        // ------------------------------------------
+        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='vivaldi'").size());
+        assertTrue(userService.read(3L).getResources().contains(RESOURCE_NAME_TESTDB2));
+        jdbcTemplate.execute("DELETE FROM test2 WHERE ID='vivaldi'");
+        // ------------------------------------------
+
+        // ------------------------------------------
+        // Unmatching --> Provision
+        // ------------------------------------------
+        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='bellini'").size());
+        assertFalse(userService.read(4L).getResources().contains(RESOURCE_NAME_TESTDB2));
+        jdbcTemplate.execute("DELETE FROM test2 WHERE ID='bellini'");
+        // ------------------------------------------
+
+        // ------------------------------------------
+        // Unmatching --> Unlink
+        // ------------------------------------------
+        assertEquals(0, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='puccini'").size());
+        assertFalse(userService.read(5L).getResources().contains(RESOURCE_NAME_TESTDB2));
+        // ------------------------------------------
+    }
+
+    @Test
+    public void pushMatchingUser() throws Exception {
+        assertTrue(userService.read(1L).getResources().contains(RESOURCE_NAME_TESTDB2));
+        assertFalse(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
+
+        final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='verdi'").size());
+        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='rossini'").size());
+
+        // ------------------------------------------
+        // Matching --> Deprovision --> dryRuyn
+        // ------------------------------------------
+        execSyncTask(19L, 50, true);
+        assertTrue(userService.read(1L).getResources().contains(RESOURCE_NAME_TESTDB2));
+        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='rossini'").size());
+        // ------------------------------------------
+
+        final Set<Long> pushTaskIds = new HashSet<>();
+        pushTaskIds.add(18L);
+        pushTaskIds.add(19L);
+        pushTaskIds.add(16L);
+
+        execSyncTasks(pushTaskIds, 50, false);
+
+        // ------------------------------------------
+        // Matching --> Deprovision && Ignore
+        // ------------------------------------------
+        assertFalse(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
+        // DELETE Capability not available ....
+        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='verdi'").size());
+        // ------------------------------------------
+
+        // ------------------------------------------
+        // Matching --> Unassign
+        // ------------------------------------------
+        assertFalse(userService.read(1L).getResources().contains(RESOURCE_NAME_TESTDB2));
+        // DELETE Capability not available ....
+        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='rossini'").size());
+        // ------------------------------------------
+
+        // ------------------------------------------
+        // Matching --> Link
+        // ------------------------------------------
+        execSyncTask(20L, 50, false);
+        assertTrue(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
+        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='verdi'").size());
+        // ------------------------------------------
+
+        pushTaskIds.clear();
+        pushTaskIds.add(21L);
+        pushTaskIds.add(22L);
+
+        execSyncTasks(pushTaskIds, 50, false);
+
+        // ------------------------------------------
+        // Matching --> Unlink && Update
+        // ------------------------------------------
+        assertFalse(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
+        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='verdi'").size());
+        // ------------------------------------------
+    }
+
+    @Test
+    public void issueSYNCOPE313DB() throws Exception {
+        // 1. create user in DB
+        UserTO user = UserITCase.getUniqueSampleTO("syncope313-db@syncope.apache.org");
+        user.setPassword("security");
+        user.getResources().add(RESOURCE_NAME_TESTDB);
+        user = createUser(user);
+        assertNotNull(user);
+        assertFalse(user.getResources().isEmpty());
+
+        // 2. Check that the DB resource has the correct password
+        final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+        String value = jdbcTemplate.queryForObject(
+                "SELECT PASSWORD FROM test WHERE ID=?", String.class, user.getUsername());
+        assertEquals(Encryptor.getInstance().encode("security", CipherAlgorithm.SHA1), value.toUpperCase());
+
+        // 3. Update the password in the DB
+        String newPassword = Encryptor.getInstance().encode("new-security", CipherAlgorithm.SHA1);
+        jdbcTemplate.execute(
+                "UPDATE test set PASSWORD='" + newPassword + "' where ID='" + user.getUsername() + "'");
+
+        // 4. Sync the user from the resource
+        SyncTaskTO syncTask = new SyncTaskTO();
+        syncTask.setName("DB Sync Task");
+        syncTask.setPerformCreate(true);
+        syncTask.setPerformUpdate(true);
+        syncTask.setFullReconciliation(true);
+        syncTask.setResource(RESOURCE_NAME_TESTDB);
+        syncTask.getActionsClassNames().add(DBPasswordSyncActions.class.getName());
+        Response taskResponse = taskService.create(syncTask);
+
+        SyncTaskTO actual = getObject(taskResponse.getLocation(), TaskService.class, SyncTaskTO.class);
+        assertNotNull(actual);
+
+        syncTask = taskService.read(actual.getKey());
+        assertNotNull(syncTask);
+        assertEquals(actual.getKey(), syncTask.getKey());
+        assertEquals(actual.getJobClassName(), syncTask.getJobClassName());
+
+        TaskExecTO execution = execSyncTask(syncTask.getKey(), 50, false);
+        final String status = execution.getStatus();
+        assertNotNull(status);
+        assertTrue(PropagationTaskExecStatus.valueOf(status).isSuccessful());
+
+        // 5. Test the sync'd user
+        UserTO updatedUser = userService.read(user.getKey());
+        assertEquals(newPassword, updatedUser.getPassword());
+
+        // 6. Delete SyncTask + user
+        taskService.delete(syncTask.getKey());
+        deleteUser(user.getKey());
+    }
+
+    @Test
+    public void issueSYNCOPE313LDAP() throws Exception {
+        // First of all, clear any potential conflict with existing user / role
+        ldapCleanup();
+
+        // 1. create user in LDAP
+        UserTO user = UserITCase.getUniqueSampleTO("syncope313-ldap@syncope.apache.org");
+        user.setPassword("security");
+        user.getResources().add(RESOURCE_NAME_LDAP);
+        user = createUser(user);
+        assertNotNull(user);
+        assertFalse(user.getResources().isEmpty());
+
+        // 2. request to change password only on Syncope and not on LDAP
+        UserMod userMod = new UserMod();
+        userMod.setKey(user.getKey());
+        userMod.setPassword("new-security");
+        StatusMod pwdPropRequest = new StatusMod();
+        pwdPropRequest.setOnSyncope(true);
+        pwdPropRequest.getResourceNames().clear();
+        userMod.setPwdPropRequest(pwdPropRequest);
+        updateUser(userMod);
+
+        // 3. Check that the Syncope user now has the changed password
+        UserTO updatedUser = userService.read(user.getKey());
+        String encodedNewPassword = Encryptor.getInstance().encode("new-security", CipherAlgorithm.SHA1);
+        assertEquals(encodedNewPassword, updatedUser.getPassword());
+
+        // 4. Check that the LDAP resource has the old password
+        ConnObjectTO connObject =
+                resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.USER, user.getKey());
+        assertNotNull(getLdapRemoteObject(
+                connObject.getPlainAttrMap().get(Name.NAME).getValues().get(0),
+                "security",
+                connObject.getPlainAttrMap().get(Name.NAME).getValues().get(0)));
+
+        // 5. Update the LDAP Connector to retrieve passwords
+        ResourceTO ldapResource = resourceService.read(RESOURCE_NAME_LDAP);
+        ConnInstanceTO resourceConnector = connectorService.read(ldapResource.getConnectorId());
+        ConnConfProperty property = resourceConnector.getConfigurationMap().get("retrievePasswordsWithSearch");
+        property.getValues().clear();
+        property.getValues().add(Boolean.TRUE);
+        connectorService.update(ldapResource.getConnectorId(), resourceConnector);
+
+        // 6. Sync the user from the resource
+        SyncTaskTO syncTask = new SyncTaskTO();
+        syncTask.setName("LDAP Sync Task");
+        syncTask.setPerformCreate(true);
+        syncTask.setPerformUpdate(true);
+        syncTask.setFullReconciliation(true);
+        syncTask.setResource(RESOURCE_NAME_LDAP);
+        syncTask.getActionsClassNames().add(LDAPPasswordSyncActions.class.getName());
+        Response taskResponse = taskService.create(syncTask);
+
+        SyncTaskTO actual = getObject(taskResponse.getLocation(), TaskService.class, SyncTaskTO.class);
+        assertNotNull(actual);
+
+        syncTask = taskService.read(actual.getKey());
+        assertNotNull(syncTask);
+        assertEquals(actual.getKey(), syncTask.getKey());
+        assertEquals(actual.getJobClassName(), syncTask.getJobClassName());
+
+        TaskExecTO execution = execSyncTask(syncTask.getKey(), 50, false);
+        final String status = execution.getStatus();
+        assertNotNull(status);
+        assertTrue(PropagationTaskExecStatus.valueOf(status).isSuccessful());
+
+        // 7. Test the sync'd user
+        String syncedPassword = Encryptor.getInstance().encode("security", CipherAlgorithm.SHA1);
+        updatedUser = userService.read(user.getKey());
+        assertEquals(syncedPassword, updatedUser.getPassword());
+
+        // 8. Delete SyncTask + user + reset the connector
+        taskService.delete(syncTask.getKey());
+        property.getValues().clear();
+        property.getValues().add(Boolean.FALSE);
+        connectorService.update(ldapResource.getConnectorId(), resourceConnector);
+        deleteUser(updatedUser.getKey());
+    }
+
+    @Test
+    public void issueSYNCOPE598() {
+        // create a new role schema
+        final PlainSchemaTO schemaTO = new PlainSchemaTO();
+        schemaTO.setKey("LDAPGroupName" + getUUIDString());
+        schemaTO.setType(AttrSchemaType.String);
+        schemaTO.setMandatoryCondition("true");
+
+        final PlainSchemaTO newPlainSchemaTO = createSchema(AttributableType.ROLE, SchemaType.PLAIN, schemaTO);
+        assertEquals(schemaTO, newPlainSchemaTO);
+
+        // create a new sample role
+        RoleTO roleTO = new RoleTO();
+        roleTO.setName("all" + getUUIDString());
+        roleTO.setParent(8L);
+
+        roleTO.getRAttrTemplates().add(newPlainSchemaTO.getKey());
+        roleTO.getPlainAttrs().add(attrTO(newPlainSchemaTO.getKey(), "all"));
+
+        roleTO = createRole(roleTO);
+        assertNotNull(roleTO);
+
+        String resourceName = "resource-ldap-roleonly";
+        ResourceTO newResourceTO = null;
+
+        try {
+            // Create resource ad-hoc
+            ResourceTO resourceTO = new ResourceTO();
+            resourceTO.setKey(resourceName);
+            resourceTO.setConnectorId(105L);
+
+            final MappingTO umapping = new MappingTO();
+            MappingItemTO item = new MappingItemTO();
+            item.setIntMappingType(IntMappingType.Username);
+            item.setExtAttrName("cn");
+            item.setAccountid(true);
+            item.setPurpose(MappingPurpose.PROPAGATION);
+            item.setMandatoryCondition("true");
+            umapping.setAccountIdItem(item);
+
+            item = new MappingItemTO();
+            item.setIntMappingType(IntMappingType.UserPlainSchema);
+            item.setExtAttrName("surname");
+            item.setIntAttrName("sn");
+            item.setPurpose(MappingPurpose.BOTH);
+            umapping.addItem(item);
+
+            item = new MappingItemTO();
+            item.setIntMappingType(IntMappingType.UserPlainSchema);
+            item.setExtAttrName("email");
+            item.setIntAttrName("mail");
+            item.setPurpose(MappingPurpose.BOTH);
+            umapping.addItem(item);
+
+            item = new MappingItemTO();
+            item.setIntMappingType(IntMappingType.Password);
+            item.setPassword(true);
+            item.setPurpose(MappingPurpose.BOTH);
+            item.setMandatoryCondition("true");
+            umapping.addItem(item);
+
+            umapping.setAccountLink("'cn=' + username + ',ou=people,o=isp'");
+
+            final MappingTO rmapping = new MappingTO();
+
+            item = new MappingItemTO();
+            item.setIntMappingType(IntMappingType.RolePlainSchema);
+            item.setExtAttrName("cn");
+            item.setIntAttrName(newPlainSchemaTO.getKey());
+            item.setAccountid(true);
+            item.setPurpose(MappingPurpose.BOTH);
+            rmapping.setAccountIdItem(item);
+
+            rmapping.setAccountLink("'cn=' + " + newPlainSchemaTO.getKey() + " + ',ou=groups,o=isp'");
+
+            resourceTO.setRmapping(rmapping);
+
+            Response response = resourceService.create(resourceTO);
+            newResourceTO = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+
+            assertNotNull(newResourceTO);
+            assertNull(newResourceTO.getUmapping());
+            assertNotNull(newResourceTO.getRmapping());
+
+            // create push task ad-hoc
+            final PushTaskTO task = new PushTaskTO();
+            task.setName("issueSYNCOPE598");
+            task.setResource(resourceName);
+            task.setPerformCreate(true);
+            task.setPerformDelete(true);
+            task.setPerformUpdate(true);
+            task.setUnmatchingRule(UnmatchingRule.ASSIGN);
+            task.setMatchingRule(MatchingRule.UPDATE);
+
+            response = taskService.create(task);
+            final PushTaskTO push = getObject(response.getLocation(), TaskService.class, PushTaskTO.class);
+
+            assertNotNull(push);
+
+            // execute the new task
+            final TaskExecTO pushExec = execSyncTask(push.getKey(), 50, false);
+            assertTrue(PropagationTaskExecStatus.valueOf(pushExec.getStatus()).isSuccessful());
+        } finally {
+            roleService.delete(roleTO.getKey());
+            if (newResourceTO != null) {
+                resourceService.delete(resourceName);
+            }
+        }
+    }
+}


[14/15] syncope git commit: FIT server integration tests

Posted by il...@apache.org.
FIT server integration tests


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/80589a1b
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/80589a1b
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/80589a1b

Branch: refs/heads/2_0_X
Commit: 80589a1b9b384f07670afe4f1581c3ceb2794501
Parents: b7d9add
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Fri Jan 23 17:40:48 2015 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Fri Jan 23 17:40:48 2015 +0100

----------------------------------------------------------------------
 .../syncope/core/util/ContentExporter.java      |    8 +-
 pom.xml                                         |    1 +
 .../common/lib/AttributableOperations.java      |   28 +-
 .../lib/annotation/FormAttributeField.java      |    2 +-
 .../common/lib/mod/AbstractAttributableMod.java |    4 +-
 .../lib/report/AbstractReportletConf.java       |    3 +-
 .../common/lib/report/RoleReportletConf.java    |   18 +-
 .../common/lib/report/UserReportletConf.java    |   18 +-
 .../syncope/common/lib/to/ConnObjectTO.java     |    2 +-
 .../apache/syncope/common/lib/to/LoggerTO.java  |   10 +-
 .../apache/syncope/common/lib/to/RoleTO.java    |   10 +-
 .../common/lib/types/ClientExceptionType.java   |   21 +-
 .../common/lib/types/IntMappingType.java        |   12 +-
 syncope620/common/rest-api/pom.xml              |    1 +
 .../common/rest/api/CollectionWrapper.java      |    4 +-
 .../syncope/common/rest/api/RESTHeaders.java    |    2 +-
 .../common/rest/api/service/ReportService.java  |   18 +-
 .../rest/api/service/ResourceService.java       |    2 +-
 .../common/rest/api/service/RoleService.java    |    5 +-
 .../rest/api/service/UserSelfService.java       |    6 +-
 .../rest/api/service/WorkflowService.java       |    4 +-
 syncope620/fit/reference/pom.xml                |  121 +
 .../DoubleValueAttributableTransformer.java     |   75 +
 .../fit/server/reference/TestSyncActions.java   |   83 +
 .../fit/server/reference/TestSyncRule.java      |   37 +
 .../src/main/resources/connid.properties        |    5 +
 .../src/main/resources/logic.properties         |   18 +
 .../fit/server/reference/AbstractITCase.java    |  365 +++
 .../fit/server/reference/ActivitiDetector.java  |   36 +
 .../server/reference/AuthenticationITCase.java  |  440 ++++
 .../server/reference/ConfigurationITCase.java   |  210 ++
 .../fit/server/reference/ConnectorITCase.java   |  723 ++++++
 .../fit/server/reference/DerSchemaITCase.java   |  151 ++
 .../fit/server/reference/LoggerITCase.java      |  215 ++
 .../server/reference/NotificationITCase.java    |  172 ++
 .../fit/server/reference/PlainSchemaITCase.java |  317 +++
 .../fit/server/reference/PolicyITCase.java      |  238 ++
 .../fit/server/reference/ReportITCase.java      |  252 ++
 .../fit/server/reference/ResourceITCase.java    |  590 +++++
 .../fit/server/reference/RoleITCase.java        |  797 ++++++
 .../fit/server/reference/SearchITCase.java      |  182 ++
 .../reference/SecurityQuestionITCase.java       |   99 +
 .../fit/server/reference/TaskITCase.java        | 1385 ++++++++++
 .../fit/server/reference/UserITCase.java        | 2442 ++++++++++++++++++
 .../fit/server/reference/UserSelfITCase.java    |  342 +++
 .../server/reference/UserWorkflowITCase.java    |  299 +++
 .../fit/server/reference/VirAttrITCase.java     |  872 +++++++
 .../fit/server/reference/VirSchemaITCase.java   |  122 +
 .../fit/server/reference/WorkflowITCase.java    |   86 +
 .../reference/src/test/resources/favicon.jpg    |  Bin 0 -> 557 bytes
 .../fit/reference/src/test/resources/test.csv   |   10 +
 .../src/test/resources/testJDBCContext.xml      |   33 +
 syncope620/pom.xml                              |  144 +-
 .../syncope/server/logic/LoggerLogic.java       |    8 +-
 .../syncope/server/logic/ReportLogic.java       |   40 +-
 .../server/logic/SpringBeanJobFactory.java      |   97 -
 .../apache/syncope/server/logic/TaskLogic.java  |    6 +-
 .../apache/syncope/server/logic/UserLogic.java  |    2 +-
 .../server/logic/init/JobInstanceLoader.java    |   41 -
 .../logic/init/JobInstanceLoaderImpl.java       |    1 +
 .../server/logic/report/RoleReportlet.java      |    6 +-
 .../server/logic/report/UserReportlet.java      |    8 +-
 .../syncope/server/logic/NotificationTest.java  |   16 +-
 .../logic/src/test/resources/logicTest.xml      |    2 +-
 .../syncope/server/misc/ConnObjectUtil.java     |    8 +-
 .../apache/syncope/server/misc/MappingUtil.java |   18 +-
 .../misc/policy/AccountPolicyEnforcer.java      |    1 +
 .../misc/policy/PasswordPolicyEnforcer.java     |    7 +-
 .../server/misc/policy/UserSuspender.java       |   26 -
 .../server/misc/security/AuthContextUtil.java   |    2 +-
 .../jpa/content/XMLContentExporter.java         |    9 +-
 .../jpa/dao/JPASubjectSearchDAO.java            |   29 +-
 .../server/persistence/jpa/dao/JPATaskDAO.java  |    2 +-
 .../server/persistence/jpa/dao/JPAUserDAO.java  |    3 +
 .../jpa/entity/AbstractAttrTemplate.java        |   14 -
 .../jpa/entity/AbstractDerAttrTemplate.java     |   41 +
 .../jpa/entity/AbstractPlainAttrTemplate.java   |   27 +
 .../jpa/entity/AbstractPlainSchema.java         |    3 +-
 .../jpa/entity/AbstractVirAttrTemplate.java     |   41 +
 .../jpa/entity/JPAAttributableUtil.java         |   12 +-
 .../persistence/jpa/entity/JPAConnInstance.java |    4 +-
 .../persistence/jpa/entity/JPAReport.java       |    2 -
 .../entity/membership/JPAMDerAttrTemplate.java  |    4 +-
 .../membership/JPAMPlainAttrTemplate.java       |   13 +-
 .../entity/membership/JPAMVirAttrTemplate.java  |    4 +-
 .../jpa/entity/role/JPARDerAttrTemplate.java    |    4 +-
 .../jpa/entity/role/JPARPlainAttrTemplate.java  |   13 +-
 .../jpa/entity/role/JPARVirAttrTemplate.java    |    4 +-
 .../jpa/entity/task/JPATaskUtil.java            |   32 +-
 .../entity/EntityValidationListener.java        |   28 +-
 .../src/main/resources/META-INF/orm.xml         |   20 +-
 .../jpa/entity/AttributableSearchTest.java      |   30 +-
 .../jpa/entity/NotificationTest.java            |    6 +-
 .../persistence/jpa/entity/ResourceTest.java    |   10 +-
 .../jpa/relationship/ResourceTest.java          |    7 +-
 .../src/test/resources/content.xml              |  100 +-
 .../server/provisioning/api/UserSuspender.java  |   26 +
 .../provisioning/api/job/JobInstanceLoader.java |   41 +
 .../api/sync/ProvisioningActions.java           |    5 +-
 .../api/sync/RolePushResultHandler.java         |   23 +
 .../api/sync/RoleSyncResultHandler.java         |   26 +
 .../api/sync/SyncopePushResultHandler.java      |   26 +
 .../api/sync/SyncopeSyncResultHandler.java      |   29 +
 .../api/sync/UserPushResultHandler.java         |   23 +
 .../api/sync/UserSyncResultHandler.java         |   23 +
 .../provisioning/java/AsyncConnectorFacade.java |   26 +-
 .../provisioning/java/ConnectorFacadeProxy.java |   13 +-
 .../java/DefaultRoleProvisioningManager.java    |    2 +-
 .../java/DefaultUserProvisioningManager.java    |   27 +-
 .../provisioning/java/UserSuspenderImpl.java    |   51 +
 .../data/AbstractAttributableDataBinder.java    |    8 +-
 .../java/data/ReportDataBinderImpl.java         |   14 +-
 .../java/data/RoleDataBinderImpl.java           |   12 +-
 .../java/data/SchemaDataBinderImpl.java         |   13 +-
 .../java/data/TaskDataBinderImpl.java           |   16 +-
 .../java/job/SpringBeanJobFactory.java          |   97 +
 .../java/notification/NotificationManager.java  |    2 +-
 .../PriorityPropagationTaskExecutor.java        |    4 +-
 .../java/sync/AbstractProvisioningJob.java      |    7 +-
 .../java/sync/AbstractPushResultHandler.java    |  374 +++
 .../sync/AbstractSubjectPushResultHandler.java  |  371 ---
 .../sync/AbstractSubjectSyncResultHandler.java  |  624 -----
 .../java/sync/AbstractSyncResultHandler.java    |  617 +++++
 .../java/sync/DefaultPushActions.java           |    6 +-
 .../java/sync/DefaultSyncActions.java           |    6 +-
 .../java/sync/LDAPMembershipSyncActions.java    |   16 +-
 .../provisioning/java/sync/PushJobImpl.java     |   28 +-
 .../java/sync/RolePushResultHandler.java        |  154 --
 .../java/sync/RolePushResultHandlerImpl.java    |  155 ++
 .../java/sync/RoleSyncResultHandler.java        |  167 --
 .../java/sync/RoleSyncResultHandlerImpl.java    |  169 ++
 .../provisioning/java/sync/SyncJobImpl.java     |   32 +-
 .../provisioning/java/sync/SyncUtilities.java   |   27 +-
 .../java/sync/UserPushResultHandler.java        |  159 --
 .../java/sync/UserPushResultHandlerImpl.java    |  160 ++
 .../java/sync/UserSyncResultHandler.java        |  155 --
 .../java/sync/UserSyncResultHandlerImpl.java    |  149 ++
 .../main/resources/mailTemplates/optin.html.vm  |    8 +-
 .../main/resources/mailTemplates/optin.txt.vm   |    8 +-
 .../src/main/resources/provisioningContext.xml  |   10 +-
 .../java/data/ResourceDataBinderTest.java       |    2 +-
 .../rest/cxf/service/AbstractServiceImpl.java   |    4 +-
 .../rest/cxf/service/LoggerServiceImpl.java     |    2 +-
 .../rest/cxf/service/ReportServiceImpl.java     |   12 +-
 .../cxf/service/RestServiceExceptionMapper.java |    3 +-
 145 files changed, 13494 insertions(+), 2229 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/core/src/main/java/org/apache/syncope/core/util/ContentExporter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/util/ContentExporter.java b/core/src/main/java/org/apache/syncope/core/util/ContentExporter.java
index e7fd35b..84e2bc4 100644
--- a/core/src/main/java/org/apache/syncope/core/util/ContentExporter.java
+++ b/core/src/main/java/org/apache/syncope/core/util/ContentExporter.java
@@ -70,9 +70,9 @@ public class ContentExporter extends AbstractContentDealer {
                 "SYNCOPEUSER", "UATTR", "UATTRVALUE", "UATTRUNIQUEVALUE", "UDERATTR", "UVIRATTR",
                 "MEMBERSHIP", "MATTR", "MATTRVALUE", "MATTRUNIQUEVALUE", "MDERATTR", "MVIRATTR"
             }));
-    
+
     protected final static Set<String> TABLE_SUFFIXES_TO_BE_INCLUDED =
-            new HashSet<String>(Arrays.asList(new String[] {"TEMPLATE"}));
+            new HashSet<String>(Arrays.asList(new String[] { "TEMPLATE" }));
 
     protected static final Map<String, String> TABLES_TO_BE_FILTERED =
             Collections.singletonMap("TASK", "DTYPE <> 'PropagationTask'");
@@ -85,10 +85,10 @@ public class ContentExporter extends AbstractContentDealer {
         for (String prefix : TABLE_PREFIXES_TO_BE_EXCLUDED) {
             if (tableName.toUpperCase().startsWith(prefix)) {
                 for (String suffix : TABLE_SUFFIXES_TO_BE_INCLUDED) {
-                    if (!tableName.toUpperCase().endsWith(suffix)) {                       
+                    if (!tableName.toUpperCase().endsWith(suffix)) {
                         allowed = false;
                     }
-                }               
+                }
             }
         }
         return allowed;

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 924b4aa..26a6cb2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1428,6 +1428,7 @@ under the License.
             <exclude>**/.*</exclude>
             <exclude>**/deb/control/conffiles</exclude>
             <exclude>**/deb/control/control</exclude>
+            <exclude>**/syncope620/**</exclude>
           </excludes>
         </configuration>
         <executions>

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/AttributableOperations.java
----------------------------------------------------------------------
diff --git a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/AttributableOperations.java b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/AttributableOperations.java
index 983cee4..ad5d322 100644
--- a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/AttributableOperations.java
+++ b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/AttributableOperations.java
@@ -81,7 +81,7 @@ public final class AttributableOperations {
                 if (virtuals) {
                     result.getVirAttrsToUpdate().add(mod);
                 } else {
-                    result.getAttrsToUpdate().add(mod);
+                    result.getPlainAttrsToUpdate().add(mod);
                 }
             } else if (!updatedValues.equals(originalValues)) {
                 // avoid unwanted inputs
@@ -93,7 +93,7 @@ public final class AttributableOperations {
                         if (virtuals) {
                             result.getVirAttrsToRemove().add(mod.getSchema());
                         } else {
-                            result.getAttrsToRemove().add(mod.getSchema());
+                            result.getPlainAttrsToRemove().add(mod.getSchema());
                         }
                     }
                 }
@@ -104,7 +104,7 @@ public final class AttributableOperations {
                     if (virtuals) {
                         result.getVirAttrsToUpdate().add(mod);
                     } else {
-                        result.getAttrsToUpdate().add(mod);
+                        result.getPlainAttrsToUpdate().add(mod);
                     }
                 }
             }
@@ -124,15 +124,15 @@ public final class AttributableOperations {
         result.setKey(updated.getKey());
 
         // 2. attributes
-        Map<String, AttrTO> updatedAttrs = new HashMap<>(updated.getAttrMap());
-        Map<String, AttrTO> originalAttrs = new HashMap<>(original.getAttrMap());
+        Map<String, AttrTO> updatedAttrs = new HashMap<>(updated.getPlainAttrMap());
+        Map<String, AttrTO> originalAttrs = new HashMap<>(original.getPlainAttrMap());
 
         Set<String> originalAttrNames = new HashSet<>(originalAttrs.keySet());
         originalAttrNames.removeAll(updatedAttrs.keySet());
 
         if (!incremental) {
-            result.getAttrsToRemove().clear();
-            result.getAttrsToRemove().addAll(originalAttrNames);
+            result.getPlainAttrsToRemove().clear();
+            result.getPlainAttrsToRemove().addAll(originalAttrNames);
         }
 
         Set<String> emptyUpdatedAttrs = new HashSet<>();
@@ -144,7 +144,7 @@ public final class AttributableOperations {
         }
         for (String emptyUpdatedAttr : emptyUpdatedAttrs) {
             updatedAttrs.remove(emptyUpdatedAttr);
-            result.getAttrsToRemove().add(emptyUpdatedAttr);
+            result.getPlainAttrsToRemove().add(emptyUpdatedAttr);
         }
 
         populate(updatedAttrs, originalAttrs, result);
@@ -269,8 +269,8 @@ public final class AttributableOperations {
                     attrMod.getValuesToBeAdded().addAll(attr.getValues());
 
                     if (!attrMod.isEmpty()) {
-                        membMod.getAttrsToUpdate().add(attrMod);
-                        membMod.getAttrsToRemove().add(attrMod.getSchema());
+                        membMod.getPlainAttrsToUpdate().add(attrMod);
+                        membMod.getPlainAttrsToRemove().add(attrMod.getSchema());
                     }
                 }
                 for (AttrTO attr : entry.getValue().getDerAttrs()) {
@@ -283,7 +283,7 @@ public final class AttributableOperations {
 
                     if (!attrMod.isEmpty()) {
                         membMod.getVirAttrsToUpdate().add(attrMod);
-                        membMod.getAttrsToRemove().add(attrMod.getSchema());
+                        membMod.getPlainAttrsToRemove().add(attrMod.getSchema());
                     }
                 }
             }
@@ -333,7 +333,7 @@ public final class AttributableOperations {
         result.setInheritTemplates(updated.isInheritTemplates());
         result.setInheritAccountPolicy(updated.isInheritAccountPolicy());
         result.setInheritPasswordPolicy(updated.isInheritPasswordPolicy());
-        result.setInheritPlainAttrs(updated.isInheritAttrs());
+        result.setInheritPlainAttrs(updated.isInheritPlainAttrs());
         result.setInheritDerAttrs(updated.isInheritDerAttrs());
         result.setInheritVirAttrs(updated.isInheritVirAttrs());
 
@@ -448,8 +448,8 @@ public final class AttributableOperations {
             final K mod, final T result) {
 
         // 1. attributes
-        result.getPlainAttrs().addAll(getUpdateValues(to.getAttrMap(),
-                mod.getAttrsToRemove(), mod.getAttrsToUpdate()));
+        result.getPlainAttrs().addAll(getUpdateValues(to.getPlainAttrMap(),
+                mod.getPlainAttrsToRemove(), mod.getPlainAttrsToUpdate()));
 
         // 2. derived attributes
         Map<String, AttrTO> attrs = to.getDerAttrMap();

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/annotation/FormAttributeField.java
----------------------------------------------------------------------
diff --git a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/annotation/FormAttributeField.java b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/annotation/FormAttributeField.java
index f007020..2c1c980 100644
--- a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/annotation/FormAttributeField.java
+++ b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/annotation/FormAttributeField.java
@@ -32,5 +32,5 @@ public @interface FormAttributeField {
 
     boolean roleSearch() default false;
 
-    IntMappingType schema() default IntMappingType.UserSchema;
+    IntMappingType schema() default IntMappingType.UserPlainSchema;
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/mod/AbstractAttributableMod.java
----------------------------------------------------------------------
diff --git a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/mod/AbstractAttributableMod.java b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/mod/AbstractAttributableMod.java
index 5d6b982..aa5d442 100644
--- a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/mod/AbstractAttributableMod.java
+++ b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/mod/AbstractAttributableMod.java
@@ -61,14 +61,14 @@ public abstract class AbstractAttributableMod extends AbstractBaseBean {
     @XmlElementWrapper(name = "plainAttrsToRemove")
     @XmlElement(name = "attribute")
     @JsonProperty("plainAttrsToRemove")
-    public Set<String> getAttrsToRemove() {
+    public Set<String> getPlainAttrsToRemove() {
         return plainAttrsToRemove;
     }
 
     @XmlElementWrapper(name = "plainAttrsToUpdate")
     @XmlElement(name = "attributeMod")
     @JsonProperty("plainAttrsToUpdate")
-    public Set<AttrMod> getAttrsToUpdate() {
+    public Set<AttrMod> getPlainAttrsToUpdate() {
         return plainAttrsToUpdate;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/report/AbstractReportletConf.java
----------------------------------------------------------------------
diff --git a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/report/AbstractReportletConf.java b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/report/AbstractReportletConf.java
index 6efdb32..0082591 100644
--- a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/report/AbstractReportletConf.java
+++ b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/report/AbstractReportletConf.java
@@ -20,6 +20,7 @@ package org.apache.syncope.common.lib.report;
 
 import javax.xml.bind.annotation.XmlSeeAlso;
 import javax.xml.bind.annotation.XmlType;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.AbstractBaseBean;
 
 @XmlType
@@ -31,7 +32,7 @@ public abstract class AbstractReportletConf extends AbstractBaseBean implements
     private String name;
 
     public AbstractReportletConf() {
-        this("");
+        this(StringUtils.EMPTY);
         setName(getClass().getName());
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/report/RoleReportletConf.java
----------------------------------------------------------------------
diff --git a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/report/RoleReportletConf.java b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/report/RoleReportletConf.java
index 295316b..e91252d 100644
--- a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/report/RoleReportletConf.java
+++ b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/report/RoleReportletConf.java
@@ -52,16 +52,16 @@ public class RoleReportletConf extends AbstractReportletConf {
     @FormAttributeField(userSearch = true)
     private String matchingCond;
 
-    @FormAttributeField(schema = IntMappingType.RoleSchema)
-    private final List<String> attrs = new ArrayList<String>();
+    @FormAttributeField(schema = IntMappingType.RolePlainSchema)
+    private final List<String> attrs = new ArrayList<>();
 
     @FormAttributeField(schema = IntMappingType.RoleDerivedSchema)
-    private final List<String> derAttrs = new ArrayList<String>();
+    private final List<String> derAttrs = new ArrayList<>();
 
     @FormAttributeField(schema = IntMappingType.RoleVirtualSchema)
-    private final List<String> virAttrs = new ArrayList<String>();
+    private final List<String> virAttrs = new ArrayList<>();
 
-    private final List<Feature> features = new ArrayList<Feature>();
+    private final List<Feature> features = new ArrayList<>();
 
     public RoleReportletConf() {
         super();
@@ -71,10 +71,10 @@ public class RoleReportletConf extends AbstractReportletConf {
         super(name);
     }
 
-    @XmlElementWrapper(name = "attributes")
-    @XmlElement(name = "attribute")
-    @JsonProperty("attributes")
-    public List<String> getAttrs() {
+    @XmlElementWrapper(name = "plainAttributes")
+    @XmlElement(name = "plainAttribute")
+    @JsonProperty("plainAttributes")
+    public List<String> getPlainAttrs() {
         return attrs;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/report/UserReportletConf.java
----------------------------------------------------------------------
diff --git a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/report/UserReportletConf.java b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/report/UserReportletConf.java
index afd806a..c82052c 100644
--- a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/report/UserReportletConf.java
+++ b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/report/UserReportletConf.java
@@ -57,16 +57,16 @@ public class UserReportletConf extends AbstractReportletConf {
     @FormAttributeField(userSearch = true)
     private String matchingCond;
 
-    @FormAttributeField(schema = IntMappingType.UserSchema)
-    private final List<String> attrs = new ArrayList<String>();
+    @FormAttributeField(schema = IntMappingType.UserPlainSchema)
+    private final List<String> attrs = new ArrayList<>();
 
     @FormAttributeField(schema = IntMappingType.UserDerivedSchema)
-    private final List<String> derAttrs = new ArrayList<String>();
+    private final List<String> derAttrs = new ArrayList<>();
 
     @FormAttributeField(schema = IntMappingType.UserVirtualSchema)
-    private final List<String> virAttrs = new ArrayList<String>();
+    private final List<String> virAttrs = new ArrayList<>();
 
-    private final List<Feature> features = new ArrayList<Feature>();
+    private final List<Feature> features = new ArrayList<>();
 
     public UserReportletConf() {
         super();
@@ -76,10 +76,10 @@ public class UserReportletConf extends AbstractReportletConf {
         super(name);
     }
 
-    @XmlElementWrapper(name = "attributes")
-    @XmlElement(name = "attribute")
-    @JsonProperty("attributes")
-    public List<String> getAttrs() {
+    @XmlElementWrapper(name = "plainAttributes")
+    @XmlElement(name = "plainAttribute")
+    @JsonProperty("plainAttributes")
+    public List<String> getPlainAttrs() {
         return attrs;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnObjectTO.java
----------------------------------------------------------------------
diff --git a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnObjectTO.java b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnObjectTO.java
index d298609..78256ba 100644
--- a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnObjectTO.java
+++ b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnObjectTO.java
@@ -46,7 +46,7 @@ public class ConnObjectTO extends AbstractAnnotatedBean {
     }
 
     @JsonIgnore
-    public Map<String, AttrTO> getAttrMap() {
+    public Map<String, AttrTO> getPlainAttrMap() {
         Map<String, AttrTO> result = new HashMap<>(attrs.size());
         for (AttrTO attributeTO : attrs) {
             result.put(attributeTO.getSchema(), attributeTO);

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/to/LoggerTO.java
----------------------------------------------------------------------
diff --git a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/to/LoggerTO.java b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/to/LoggerTO.java
index 263ab97..810a143 100644
--- a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/to/LoggerTO.java
+++ b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/to/LoggerTO.java
@@ -29,7 +29,7 @@ public class LoggerTO extends AbstractBaseBean {
 
     private static final long serialVersionUID = -7794833835668648505L;
 
-    private String name;
+    private String key;
 
     private LoggerLevel level;
 
@@ -41,11 +41,11 @@ public class LoggerTO extends AbstractBaseBean {
         this.level = level;
     }
 
-    public String getName() {
-        return name;
+    public String getKey() {
+        return key;
     }
 
-    public void setName(final String name) {
-        this.name = name;
+    public void setKey(final String key) {
+        this.key = key;
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java
----------------------------------------------------------------------
diff --git a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java
index 6e24929..2a353cf 100644
--- a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java
+++ b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java
@@ -47,7 +47,7 @@ public class RoleTO extends AbstractSubjectTO {
 
     private boolean inheritTemplates;
 
-    private boolean inheritAttrs;
+    private boolean inheritPlainAttrs;
 
     private boolean inheritDerAttrs;
 
@@ -123,12 +123,12 @@ public class RoleTO extends AbstractSubjectTO {
         this.inheritTemplates = inheritTemplates;
     }
 
-    public boolean isInheritAttrs() {
-        return inheritAttrs;
+    public boolean isInheritPlainAttrs() {
+        return inheritPlainAttrs;
     }
 
-    public void setInheritAttrs(final boolean inheritAttrs) {
-        this.inheritAttrs = inheritAttrs;
+    public void setInheritPlainAttrs(final boolean inheritPlainAttrs) {
+        this.inheritPlainAttrs = inheritPlainAttrs;
     }
 
     public boolean isInheritDerAttrs() {

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
----------------------------------------------------------------------
diff --git a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
index 0c272a9..c55cebd 100644
--- a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
+++ b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
@@ -29,30 +29,25 @@ public enum ClientExceptionType {
     EntityExists(Response.Status.CONFLICT),
     GenericPersistence(Response.Status.BAD_REQUEST),
     InvalidSecurityAnswer(Response.Status.BAD_REQUEST),
+    InvalidEntity(Response.Status.BAD_REQUEST),
     InvalidLogger(Response.Status.BAD_REQUEST),
     InvalidConnInstance(Response.Status.BAD_REQUEST),
     InvalidConnIdConf(Response.Status.BAD_REQUEST),
     InvalidPolicy(Response.Status.BAD_REQUEST),
-    InvalidSyncopeConf(Response.Status.BAD_REQUEST),
-    InvalidSyncopeRole(Response.Status.BAD_REQUEST),
+    InvalidConf(Response.Status.BAD_REQUEST),
+    InvalidRole(Response.Status.BAD_REQUEST),
+    InvalidReport(Response.Status.BAD_REQUEST),
     InvalidReportExec(Response.Status.BAD_REQUEST),
     InvalidRoles(Response.Status.BAD_REQUEST),
     InvalidSchemaDefinition(Response.Status.BAD_REQUEST),
     InvalidSearchExpression(Response.Status.BAD_REQUEST),
     InvalidPageOrSize(Response.Status.BAD_REQUEST),
     InvalidPropagationTaskExecReport(Response.Status.BAD_REQUEST),
-    InvalidUSchema(Response.Status.BAD_REQUEST),
-    InvalidUDerSchema(Response.Status.BAD_REQUEST),
-    InvalidUVirSchema(Response.Status.BAD_REQUEST),
-    InvalidRSchema(Response.Status.BAD_REQUEST),
-    InvalidRDerSchema(Response.Status.BAD_REQUEST),
-    InvalidRVirSchema(Response.Status.BAD_REQUEST),
-    InvalidMSchema(Response.Status.BAD_REQUEST),
-    InvalidMDerSchema(Response.Status.BAD_REQUEST),
-    InvalidMVirSchema(Response.Status.BAD_REQUEST),
-    InvalidCSchema(Response.Status.BAD_REQUEST),
+    InvalidPlainSchema(Response.Status.BAD_REQUEST),
+    InvalidDerSchema(Response.Status.BAD_REQUEST),
+    InvalidVirSchema(Response.Status.BAD_REQUEST),
     InvalidSchemaMapping(Response.Status.BAD_REQUEST),
-    InvalidSyncopeUser(Response.Status.BAD_REQUEST),
+    InvalidUser(Response.Status.BAD_REQUEST),
     InvalidExternalResource(Response.Status.BAD_REQUEST),
     InvalidNotification(Response.Status.BAD_REQUEST),
     InvalidPropagationTask(Response.Status.BAD_REQUEST),

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/types/IntMappingType.java
----------------------------------------------------------------------
diff --git a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/types/IntMappingType.java b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/types/IntMappingType.java
index 5fefcc9..7dd60d6 100644
--- a/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/types/IntMappingType.java
+++ b/syncope620/common/lib/src/main/java/org/apache/syncope/common/lib/types/IntMappingType.java
@@ -35,7 +35,7 @@ public enum IntMappingType {
     // -------------------------
     // User attribute types (the same in UserMappingType)
     // -------------------------
-    UserSchema(AttributableType.USER),
+    UserPlainSchema(AttributableType.USER),
     UserDerivedSchema(AttributableType.USER),
     UserVirtualSchema(AttributableType.USER),
     UserId(AttributableType.USER),
@@ -44,7 +44,7 @@ public enum IntMappingType {
     // -------------------------
     // Role attribute types (the same in RoleMappingType)
     // -------------------------
-    RoleSchema(AttributableType.ROLE),
+    RolePlainSchema(AttributableType.ROLE),
     RoleDerivedSchema(AttributableType.ROLE),
     RoleVirtualSchema(AttributableType.ROLE),
     RoleId(AttributableType.ROLE),
@@ -53,7 +53,7 @@ public enum IntMappingType {
     // -------------------------
     // Membership attribute types (the same in MembershipMappingType)
     // -------------------------
-    MembershipSchema(AttributableType.MEMBERSHIP),
+    MembershipPlainSchema(AttributableType.MEMBERSHIP),
     MembershipDerivedSchema(AttributableType.MEMBERSHIP),
     MembershipVirtualSchema(AttributableType.MEMBERSHIP),
     MembershipId(AttributableType.MEMBERSHIP);
@@ -164,7 +164,7 @@ public enum IntMappingType {
      */
     private enum UserMappingType {
 
-        UserSchema,
+        UserPlainSchema,
         UserDerivedSchema,
         UserVirtualSchema,
         UserId,
@@ -178,7 +178,7 @@ public enum IntMappingType {
      */
     private enum RoleMappingType {
 
-        RoleSchema,
+        RolePlainSchema,
         RoleDerivedSchema,
         RoleVirtualSchema,
         RoleId,
@@ -192,7 +192,7 @@ public enum IntMappingType {
      */
     private enum MembershipMappingType {
 
-        MembershipSchema,
+        MembershipPlainSchema,
         MembershipDerivedSchema,
         MembershipVirtualSchema,
         MembershipId;

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/rest-api/pom.xml
----------------------------------------------------------------------
diff --git a/syncope620/common/rest-api/pom.xml b/syncope620/common/rest-api/pom.xml
index 967573e..addc3b9 100644
--- a/syncope620/common/rest-api/pom.xml
+++ b/syncope620/common/rest-api/pom.xml
@@ -73,6 +73,7 @@ under the License.
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-javadoc-plugin</artifactId>
+        <inherited>true</inherited>
         <executions>
           <execution>
             <id>attach-javadocs</id>

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/CollectionWrapper.java
----------------------------------------------------------------------
diff --git a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/CollectionWrapper.java b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/CollectionWrapper.java
index 070f73c..1ab407d 100644
--- a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/CollectionWrapper.java
+++ b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/CollectionWrapper.java
@@ -59,7 +59,7 @@ public final class CollectionWrapper {
         List<AuditLoggerName> respons = new ArrayList<AuditLoggerName>();
         for (LoggerTO l : logger) {
             try {
-                respons.add(AuditLoggerName.fromLoggerName(l.getName()));
+                respons.add(AuditLoggerName.fromLoggerName(l.getKey()));
             } catch (Exception ignore) {
                 // ignore
             }
@@ -71,7 +71,7 @@ public final class CollectionWrapper {
         List<LoggerTO> respons = new ArrayList<LoggerTO>();
         for (AuditLoggerName l : auditNames) {
             LoggerTO loggerTO = new LoggerTO();
-            loggerTO.setName(l.toLoggerName());
+            loggerTO.setKey(l.toLoggerName());
             loggerTO.setLevel(LoggerLevel.DEBUG);
             respons.add(loggerTO);
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/RESTHeaders.java
----------------------------------------------------------------------
diff --git a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/RESTHeaders.java b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/RESTHeaders.java
index 715270b..20e58c6 100644
--- a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/RESTHeaders.java
+++ b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/RESTHeaders.java
@@ -77,7 +77,7 @@ public final class RESTHeaders {
     /**
      * Declares the type of exception being raised.
      *
-     * @see ClientExceptionType
+     * @see org.apache.syncope.common.lib.types.ClientExceptionType
      */
     public static final String ERROR_CODE = "X-Application-Error-Code";
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
----------------------------------------------------------------------
diff --git a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
index b8f2826..1e9f943 100644
--- a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
+++ b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
@@ -72,13 +72,13 @@ public interface ReportService extends JAXRSService {
     /**
      * Returns report execution with matching key.
      *
-     * @param executionId report execution id to be selected
+     * @param executionKey report execution id to be selected
      * @return report execution with matching key
      */
     @GET
-    @Path("executions/{executionId}")
+    @Path("executions/{executionKey}")
     @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    ReportExecTO readExecution(@NotNull @PathParam("executionId") Long executionId);
+    ReportExecTO readExecution(@NotNull @PathParam("executionKey") Long executionKey);
 
     /**
      * Returns a paged list of all existing reports.
@@ -163,11 +163,11 @@ public interface ReportService extends JAXRSService {
     /**
      * Deletes report execution with matching key.
      *
-     * @param executionId key of execution report to be deleted
+     * @param executionKey key of execution report to be deleted
      */
     @DELETE
-    @Path("executions/{executionId}")
-    void deleteExecution(@NotNull @PathParam("executionId") Long executionId);
+    @Path("executions/{executionKey}")
+    void deleteExecution(@NotNull @PathParam("executionKey") Long executionKey);
 
     /**
      * Executes the report with matching key.
@@ -183,13 +183,13 @@ public interface ReportService extends JAXRSService {
     /**
      * Exports the report execution with matching key in the requested format.
      *
-     * @param executionId key of execution report to be selected
+     * @param executionKey key of execution report to be selected
      * @param fmt file-format selection
      * @return a stream for content download
      */
     @GET
-    @Path("executions/{executionId}/stream")
+    @Path("executions/{executionKey}/stream")
     @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    Response exportExecutionResult(@NotNull @PathParam("executionId") Long executionId,
+    Response exportExecutionResult(@NotNull @PathParam("executionKey") Long executionKey,
             @QueryParam("format") ReportExecExportFormat fmt);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java
----------------------------------------------------------------------
diff --git a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java
index 9b62de3..044025b 100644
--- a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java
+++ b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java
@@ -58,7 +58,7 @@ public interface ResourceService extends JAXRSService {
      * @return connector object from the external resource, for the given type and key
      */
     @GET
-    @Path("{resourceKey}/{type}/{id}")
+    @Path("{resourceKey}/{type}/{key}")
     @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
     ConnObjectTO getConnectorObject(@NotNull @PathParam("resourceKey") String resourceKey,
             @NotNull @PathParam("type") SubjectType type, @NotNull @PathParam("key") Long key);

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java
----------------------------------------------------------------------
diff --git a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java
index f0bbf08..5d1b737 100644
--- a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java
+++ b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java
@@ -264,7 +264,7 @@ public interface RoleService extends JAXRSService {
      * @param type resource association action type
      * @param resourceNames external resources to be used for propagation-related operations
      * @return <tt>Response</tt> object featuring
-     * {@link org.apache.syncope.common.reqres.BulkActionResult} as <tt>Entity</tt>
+     * {@link BulkActionResult} as <tt>Entity</tt>
      */
     @Descriptions({
         @Description(target = DocTarget.RESPONSE,
@@ -284,8 +284,7 @@ public interface RoleService extends JAXRSService {
      * @param roleKey role id.
      * @param type resource association action type
      * @param resourceNames external resources to be used for propagation-related operations
-     * @return <tt>Response</tt> object featuring {@link org.apache.syncope.common.reqres.BulkActionResult}
-     * as <tt>Entity</tt>
+     * @return <tt>Response</tt> object featuring {@link BulkActionResult} as <tt>Entity</tt>
      */
     @Descriptions({
         @Description(target = DocTarget.RESPONSE,

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java
----------------------------------------------------------------------
diff --git a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java
index cd3a302..75b6af2 100644
--- a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java
+++ b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java
@@ -48,9 +48,9 @@ public interface UserSelfService extends JAXRSService {
      *
      * @return <tt>Response</tt> contains special Syncope HTTP header indicating if user self registration and / or
      * password reset is allowed
-     * @see org.apache.syncope.common.types.RESTHeaders#SELFREG_ALLOWED
-     * @see org.apache.syncope.common.types.RESTHeaders#PWDRESET_ALLOWED
-     * @see org.apache.syncope.common.types.RESTHeaders#PWDRESET_NEEDS_SECURITYQUESTIONS
+     * @see org.apache.syncope.common.rest.api.RESTHeaders#SELFREG_ALLOWED
+     * @see org.apache.syncope.common.rest.api.RESTHeaders#PWDRESET_ALLOWED
+     * @see org.apache.syncope.common.rest.api.RESTHeaders#PWDRESET_NEEDS_SECURITYQUESTIONS
      */
     @Descriptions({
         @Description(target = DocTarget.RESPONSE,

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java
----------------------------------------------------------------------
diff --git a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java
index 709ab37..7b8aded 100644
--- a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java
+++ b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java
@@ -46,8 +46,8 @@ public interface WorkflowService extends JAXRSService {
      * @param kind user or role
      * @return <tt>Response</tt> contains special syncope HTTP header indicating if Activiti is enabled for
      * users / roles
-     * @see org.apache.syncope.common.types.RESTHeaders#ACTIVITI_USER_ENABLED
-     * @see org.apache.syncope.common.types.RESTHeaders#ACTIVITI_ROLE_ENABLED
+     * @see org.apache.syncope.common.rest.api.RESTHeaders#ACTIVITI_USER_ENABLED
+     * @see org.apache.syncope.common.rest.api.RESTHeaders#ACTIVITI_ROLE_ENABLED
      */
     @Descriptions({
         @Description(target = DocTarget.RESPONSE,

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/pom.xml
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/pom.xml b/syncope620/fit/reference/pom.xml
index 148c71a..564d8f0 100644
--- a/syncope620/fit/reference/pom.xml
+++ b/syncope620/fit/reference/pom.xml
@@ -132,6 +132,24 @@ under the License.
       <groupId>org.webjars</groupId>
       <artifactId>highlightjs</artifactId>
     </dependency>
+    
+    <!-- TEST -->
+    <dependency>
+      <groupId>org.apache.syncope.client</groupId>
+      <artifactId>syncope-client-lib</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
@@ -161,6 +179,45 @@ under the License.
       </plugin>
       
       <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <inherited>true</inherited>
+        <executions>
+          <execution>
+            <id>setupCSV</id>
+            <phase>pre-integration-test</phase>
+            <configuration>
+              <target>
+                <copy file="${project.build.directory}/test-classes/test.csv" todir="${test.csvdir.path}" overwrite="true"/>
+              </target>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+        <inherited>true</inherited>
+        <configuration>
+          <systemPropertyVariables>
+            <jaxrsContentType>${jaxrs.content.type}</jaxrsContentType>
+          </systemPropertyVariables>
+        </configuration>
+        <executions>
+          <execution>
+            <id>verify</id>
+            <goals>
+              <goal>verify</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      
+      <plugin>
         <groupId>org.codehaus.cargo</groupId>
         <artifactId>cargo-maven2-plugin</artifactId>
         <inherited>true</inherited>
@@ -270,12 +327,22 @@ under the License.
         <filtering>true</filtering>
       </resource>
     </resources>
+    <testResources>
+      <testResource>
+        <directory>src/test/resources</directory>
+        <filtering>true</filtering>
+      </testResource>
+    </testResources>
   </build>
   
   <profiles>
     <profile>
       <id>debug</id>
 
+      <properties>
+        <skipTests>true</skipTests>
+      </properties>
+
       <build>
         <defaultGoal>clean verify cargo:run</defaultGoal>
 
@@ -306,5 +373,59 @@ under the License.
         </plugins>
       </build>
     </profile>
+    
+    <profile>
+      <id>skipTests</id>
+
+      <dependencies>
+        <dependency>
+          <groupId>com.h2database</groupId>
+          <artifactId>h2</artifactId>
+        </dependency>
+      </dependencies>
+
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-failsafe-plugin</artifactId>
+            <inherited>true</inherited>
+            <configuration>
+              <skipTests>${skipTests}</skipTests>
+            </configuration>
+          </plugin>
+
+          <plugin>
+            <groupId>org.codehaus.cargo</groupId>
+            <artifactId>cargo-maven2-plugin</artifactId>
+            <inherited>true</inherited>
+            <configuration>
+              <deployables>
+                <deployable>
+                  <location>${project.build.directory}/${project.build.finalName}.war</location>
+                </deployable>
+              </deployables>
+            </configuration>
+            <executions>
+              <execution>
+                <id>install-container</id>
+                <phase>package</phase>
+                <goals>
+                  <goal>install</goal>
+                </goals>
+              </execution>
+              <execution>
+                <id>start-container</id>
+                <phase>none</phase>
+              </execution>
+              <execution>
+                <id>stop-container</id>
+                <phase>none</phase>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
   </profiles>
 </project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/main/java/org/apache/syncope/fit/server/reference/DoubleValueAttributableTransformer.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/main/java/org/apache/syncope/fit/server/reference/DoubleValueAttributableTransformer.java b/syncope620/fit/reference/src/main/java/org/apache/syncope/fit/server/reference/DoubleValueAttributableTransformer.java
new file mode 100644
index 0000000..da25a34
--- /dev/null
+++ b/syncope620/fit/reference/src/main/java/org/apache/syncope/fit/server/reference/DoubleValueAttributableTransformer.java
@@ -0,0 +1,75 @@
+/*
+ * 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.server.reference;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.syncope.common.lib.mod.AbstractAttributableMod;
+import org.apache.syncope.common.lib.mod.AttrMod;
+import org.apache.syncope.common.lib.to.AbstractAttributableTO;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.server.provisioning.api.AttributableTransformer;
+
+/**
+ * Class for integration tests: transform (by making it double) any attribute value for defined schema.
+ */
+public class DoubleValueAttributableTransformer implements AttributableTransformer {
+
+    private static final String NAME = "makeItDouble";
+
+    @Override
+    public <T extends AbstractAttributableTO> T transform(final T input) {
+        for (AttrTO attr : input.getPlainAttrs()) {
+            if (NAME.equals(attr.getSchema())) {
+                List<String> values = new ArrayList<>(attr.getValues().size());
+                for (String value : attr.getValues()) {
+                    try {
+                        values.add(String.valueOf(2 * Long.valueOf(value)));
+                    } catch (NumberFormatException e) {
+                        // ignore
+                    }
+                }
+                attr.getValues().clear();
+                attr.getValues().addAll(values);
+            }
+        }
+
+        return input;
+    }
+
+    @Override
+    public <T extends AbstractAttributableMod> T transform(final T input) {
+        for (AttrMod attr : input.getPlainAttrsToUpdate()) {
+            if (NAME.equals(attr.getSchema())) {
+                List<String> values = new ArrayList<>(attr.getValuesToBeAdded().size());
+                for (String value : attr.getValuesToBeAdded()) {
+                    try {
+                        values.add(String.valueOf(2 * Long.valueOf(value)));
+                    } catch (NumberFormatException e) {
+                        // ignore
+                    }
+                }
+                attr.getValuesToBeAdded().clear();
+                attr.getValuesToBeAdded().addAll(values);
+            }
+        }
+
+        return input;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/main/java/org/apache/syncope/fit/server/reference/TestSyncActions.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/main/java/org/apache/syncope/fit/server/reference/TestSyncActions.java b/syncope620/fit/reference/src/main/java/org/apache/syncope/fit/server/reference/TestSyncActions.java
new file mode 100644
index 0000000..01da2c9
--- /dev/null
+++ b/syncope620/fit/reference/src/main/java/org/apache/syncope/fit/server/reference/TestSyncActions.java
@@ -0,0 +1,83 @@
+/*
+ * 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.server.reference;
+
+import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
+import org.apache.syncope.common.lib.mod.AttrMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.server.provisioning.api.sync.ProvisioningProfile;
+import org.apache.syncope.server.provisioning.java.sync.DefaultSyncActions;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+import org.quartz.JobExecutionException;
+
+public class TestSyncActions extends DefaultSyncActions {
+
+    private int counter = 0;
+
+    @Override
+    public <T extends AbstractSubjectTO> SyncDelta beforeProvision(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final T subject) throws JobExecutionException {
+
+        AttrTO attrTO = null;
+        for (int i = 0; i < subject.getPlainAttrs().size(); i++) {
+            if ("fullname".equals(subject.getPlainAttrs().get(i).getSchema())) {
+                attrTO = subject.getPlainAttrs().get(i);
+            }
+        }
+        if (attrTO == null) {
+            attrTO = new AttrTO();
+            attrTO.setSchema("fullname");
+            subject.getPlainAttrs().add(attrTO);
+        }
+        attrTO.getValues().clear();
+        attrTO.getValues().add(String.valueOf(counter++));
+
+        return delta;
+    }
+
+    @Override
+    public <T extends AbstractSubjectTO, K extends AbstractSubjectMod> SyncDelta beforeUpdate(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final T subject,
+            final K subjectMod) throws JobExecutionException {
+
+        subjectMod.getPlainAttrsToRemove().add("fullname");
+
+        AttrMod fullnameMod = null;
+        for (AttrMod attrMod : subjectMod.getPlainAttrsToUpdate()) {
+            if ("fullname".equals(attrMod.getSchema())) {
+                fullnameMod = attrMod;
+            }
+        }
+        if (fullnameMod == null) {
+            fullnameMod = new AttrMod();
+            fullnameMod.setSchema("fullname");
+            subjectMod.getPlainAttrsToUpdate().add(fullnameMod);
+        }
+
+        fullnameMod.getValuesToBeAdded().clear();
+        fullnameMod.getValuesToBeAdded().add(String.valueOf(counter++));
+
+        return delta;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/main/java/org/apache/syncope/fit/server/reference/TestSyncRule.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/main/java/org/apache/syncope/fit/server/reference/TestSyncRule.java b/syncope620/fit/reference/src/main/java/org/apache/syncope/fit/server/reference/TestSyncRule.java
new file mode 100644
index 0000000..8454ec6
--- /dev/null
+++ b/syncope620/fit/reference/src/main/java/org/apache/syncope/fit/server/reference/TestSyncRule.java
@@ -0,0 +1,37 @@
+/*
+ * 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.server.reference;
+
+import org.apache.syncope.server.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.server.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.server.provisioning.api.sync.SyncCorrelationRule;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+
+public class TestSyncRule implements SyncCorrelationRule {
+
+    @Override
+    public SearchCond getSearchCond(ConnectorObject connObj) {
+        AttributeCond cond = new AttributeCond();
+        cond.setSchema("email");
+        cond.setType(AttributeCond.Type.EQ);
+        cond.setExpression(connObj.getName().getNameValue());
+
+        return SearchCond.getLeafCond(cond);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/main/resources/connid.properties
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/main/resources/connid.properties b/syncope620/fit/reference/src/main/resources/connid.properties
index 54d83c3..40d649c 100644
--- a/syncope620/fit/reference/src/main/resources/connid.properties
+++ b/syncope620/fit/reference/src/main/resources/connid.properties
@@ -16,3 +16,8 @@
 # under the License.
 connid.locations=${connid.location},\
 connid://${testconnectorserver.key}@localhost:${testconnectorserver.port}
+
+## for test only
+testdb.url=${testdb.url}
+connid.soap.version=${connid.soap.version}
+connid.db.table.version=${connid.db.table.version}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/main/resources/logic.properties
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/main/resources/logic.properties b/syncope620/fit/reference/src/main/resources/logic.properties
new file mode 100644
index 0000000..2477c4b
--- /dev/null
+++ b/syncope620/fit/reference/src/main/resources/logic.properties
@@ -0,0 +1,18 @@
+# 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.
+attributableTransformer=org.apache.syncope.fit.server.reference.DoubleValueAttributableTransformer
+logicInvocationHandler=org.apache.syncope.server.logic.LogicInvocationHandler

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/AbstractITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/AbstractITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/AbstractITCase.java
new file mode 100644
index 0000000..fc81657
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/AbstractITCase.java
@@ -0,0 +1,365 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Properties;
+import java.util.UUID;
+import javax.naming.Context;
+import javax.naming.directory.InitialDirContext;
+import javax.sql.DataSource;
+import javax.ws.rs.core.Response;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+import org.apache.syncope.common.lib.mod.AttrMod;
+import org.apache.syncope.common.lib.mod.RoleMod;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.AbstractPolicyTO;
+import org.apache.syncope.common.lib.to.AbstractSchemaTO;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.ConnConfProperty;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.ConfigurationService;
+import org.apache.syncope.common.rest.api.service.ConnectorService;
+import org.apache.syncope.common.rest.api.service.EntitlementService;
+import org.apache.syncope.common.rest.api.service.LoggerService;
+import org.apache.syncope.common.rest.api.service.NotificationService;
+import org.apache.syncope.common.rest.api.service.PolicyService;
+import org.apache.syncope.common.rest.api.service.ReportService;
+import org.apache.syncope.common.rest.api.service.ResourceService;
+import org.apache.syncope.common.rest.api.service.RoleService;
+import org.apache.syncope.common.rest.api.service.SchemaService;
+import org.apache.syncope.common.rest.api.service.SecurityQuestionService;
+import org.apache.syncope.common.rest.api.service.TaskService;
+import org.apache.syncope.common.rest.api.service.UserSelfService;
+import org.apache.syncope.common.rest.api.service.UserService;
+import org.apache.syncope.common.rest.api.service.UserWorkflowService;
+import org.apache.syncope.common.rest.api.service.WorkflowService;
+import org.identityconnectors.common.security.Encryptor;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = { "classpath:testJDBCContext.xml" })
+public abstract class AbstractITCase {
+
+    /**
+     * Logger.
+     */
+    protected static final Logger LOG = LoggerFactory.getLogger(AbstractITCase.class);
+
+    protected static final String ADMIN_UNAME = "admin";
+
+    protected static final String ADMIN_PWD = "password";
+
+    private static final String ADDRESS = "http://localhost:9080/syncope/rest";
+
+    private static final String ENV_KEY_CONTENT_TYPE = "jaxrsContentType";
+
+    protected static final SyncopeClientFactoryBean clientFactory = new SyncopeClientFactoryBean().setAddress(ADDRESS);
+
+    protected static final String RESOURCE_NAME_WS1 = "ws-target-resource-1";
+
+    protected static final String RESOURCE_NAME_WS2 = "ws-target-resource-2";
+
+    protected static final String RESOURCE_NAME_LDAP = "resource-ldap";
+
+    protected static final String RESOURCE_NAME_TESTDB = "resource-testdb";
+
+    protected static final String RESOURCE_NAME_TESTDB2 = "resource-testdb2";
+
+    protected static final String RESOURCE_NAME_CSV = "resource-csv";
+
+    protected static final String RESOURCE_NAME_DBSYNC = "resource-db-sync";
+
+    protected static final String RESOURCE_NAME_DBVIRATTR = "resource-db-virattr";
+
+    protected static final String RESOURCE_NAME_NOPROPAGATION = "ws-target-resource-nopropagation";
+
+    protected static final String RESOURCE_NAME_NOPROPAGATION2 = "ws-target-resource-nopropagation2";
+
+    protected static final String RESOURCE_NAME_NOPROPAGATION3 = "ws-target-resource-nopropagation3";
+
+    protected static final String RESOURCE_NAME_NOPROPAGATION4 = "ws-target-resource-nopropagation4";
+
+    protected static final String RESOURCE_NAME_RESETSYNCTOKEN = "ws-target-resource-update-resetsynctoken";
+
+    protected static final String RESOURCE_NAME_TIMEOUT = "ws-target-resource-timeout";
+
+    protected static final String RESOURCE_NAME_MAPPINGS1 = "ws-target-resource-list-mappings-1";
+
+    protected static final String RESOURCE_NAME_MAPPINGS2 = "ws-target-resource-list-mappings-2";
+
+    protected static final String RESOURCE_NAME_CREATE = "ws-target-resource-create";
+
+    protected static final String RESOURCE_NAME_CREATE_SINGLE = "ws-target-resource-create-single";
+
+    protected static final String RESOURCE_NAME_CREATE_WRONG = "ws-target-resource-create-wrong";
+
+    protected static final String RESOURCE_NAME_DELETE = "ws-target-resource-delete";
+
+    protected static final String RESOURCE_NAME_UPDATE = "ws-target-resource-update";
+
+    protected static final String RESOURCE_NAME_CREATE_NONE = "ws-target-resource-create-none";
+
+    protected static String ANONYMOUS_UNAME;
+
+    protected static String ANONYMOUS_KEY;
+
+    protected static SyncopeClient adminClient;
+
+    protected static UserService userService;
+
+    protected static UserSelfService userSelfService;
+
+    protected static UserWorkflowService userWorkflowService;
+
+    protected static RoleService roleService;
+
+    protected static ResourceService resourceService;
+
+    protected static EntitlementService entitlementService;
+
+    protected static ConfigurationService configurationService;
+
+    protected static ConnectorService connectorService;
+
+    protected static LoggerService loggerService;
+
+    protected static ReportService reportService;
+
+    protected static TaskService taskService;
+
+    protected static WorkflowService workflowService;
+
+    protected static NotificationService notificationService;
+
+    protected static SchemaService schemaService;
+
+    protected static PolicyService policyService;
+
+    protected static SecurityQuestionService securityQuestionService;
+
+    @Autowired
+    protected DataSource testDataSource;
+
+    @BeforeClass
+    public static void securitySetup() {
+        InputStream propStream = null;
+        try {
+            propStream = Encryptor.class.getResourceAsStream("/security.properties");
+            Properties props = new Properties();
+            props.load(propStream);
+
+            ANONYMOUS_UNAME = props.getProperty("anonymousUser");
+            ANONYMOUS_KEY = props.getProperty("anonymousKey");
+        } catch (Exception e) {
+            LOG.error("Could not read secretKey", e);
+        } finally {
+            IOUtils.closeQuietly(propStream);
+        }
+
+        assertNotNull(ANONYMOUS_UNAME);
+        assertNotNull(ANONYMOUS_KEY);
+    }
+
+    @BeforeClass
+    public static void restSetup() {
+        final String envContentType = System.getProperty(ENV_KEY_CONTENT_TYPE);
+        if (StringUtils.isNotBlank(envContentType)) {
+            clientFactory.setContentType(envContentType);
+        }
+        LOG.info("Performing IT with content type {}", clientFactory.getContentType().getMediaType());
+
+        adminClient = clientFactory.create(ADMIN_UNAME, ADMIN_PWD);
+
+        userService = adminClient.getService(UserService.class);
+        userSelfService = adminClient.getService(UserSelfService.class);
+        userWorkflowService = adminClient.getService(UserWorkflowService.class);
+        roleService = adminClient.getService(RoleService.class);
+        resourceService = adminClient.getService(ResourceService.class);
+        entitlementService = adminClient.getService(EntitlementService.class);
+        configurationService = adminClient.getService(ConfigurationService.class);
+        connectorService = adminClient.getService(ConnectorService.class);
+        loggerService = adminClient.getService(LoggerService.class);
+        reportService = adminClient.getService(ReportService.class);
+        taskService = adminClient.getService(TaskService.class);
+        policyService = adminClient.getService(PolicyService.class);
+        workflowService = adminClient.getService(WorkflowService.class);
+        notificationService = adminClient.getService(NotificationService.class);
+        schemaService = adminClient.getService(SchemaService.class);
+        securityQuestionService = adminClient.getService(SecurityQuestionService.class);
+    }
+
+    protected static String getUUIDString() {
+        return UUID.randomUUID().toString().substring(0, 8);
+    }
+
+    protected static AttrTO attrTO(final String schema, final String value) {
+        AttrTO attr = new AttrTO();
+        attr.setSchema(schema);
+        attr.getValues().add(value);
+        return attr;
+    }
+
+    protected static AttrMod attrMod(final String schema, final String valueToBeAdded) {
+        AttrMod attr = new AttrMod();
+        attr.setSchema(schema);
+        attr.getValuesToBeAdded().add(valueToBeAdded);
+        return attr;
+    }
+
+    protected UserTO createUser(final UserTO userTO) {
+        return createUser(userTO, true);
+    }
+
+    protected UserTO createUser(final UserTO userTO, final boolean storePassword) {
+        Response response = userService.create(userTO, storePassword);
+        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+            Exception ex = clientFactory.getExceptionMapper().fromResponse(response);
+            if (ex != null) {
+                throw (RuntimeException) ex;
+            }
+        }
+        return response.readEntity(UserTO.class);
+    }
+
+    protected UserTO readUser(final String username) {
+        return userService.read(Long.valueOf(
+                userService.getUserId(username).getHeaderString(RESTHeaders.USER_ID)));
+    }
+
+    protected UserTO updateUser(final UserMod userMod) {
+        return userService.update(userMod.getKey(), userMod).readEntity(UserTO.class);
+    }
+
+    protected UserTO deleteUser(final Long id) {
+        return userService.delete(id).readEntity(UserTO.class);
+    }
+
+    public <T> T getObject(final URI location, final Class<?> serviceClass, final Class<T> resultClass) {
+        WebClient webClient = WebClient.fromClient(WebClient.client(adminClient.getService(serviceClass)));
+        webClient.accept(clientFactory.getContentType().getMediaType()).to(location.toASCIIString(), false);
+
+        return webClient.get(resultClass);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected <T extends AbstractSchemaTO> T createSchema(final AttributableType kind,
+            final SchemaType type, final T schemaTO) {
+
+        Response response = schemaService.create(kind, type, schemaTO);
+        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+            Exception ex = clientFactory.getExceptionMapper().fromResponse(response);
+            if (ex != null) {
+                throw (RuntimeException) ex;
+            }
+        }
+
+        return (T) getObject(response.getLocation(), SchemaService.class, schemaTO.getClass());
+    }
+
+    protected RoleTO createRole(final RoleTO newRoleTO) {
+        Response response = roleService.create(newRoleTO);
+        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+            Exception ex = clientFactory.getExceptionMapper().fromResponse(response);
+            if (ex != null) {
+                throw (RuntimeException) ex;
+            }
+        }
+        return getObject(response.getLocation(), RoleService.class, RoleTO.class);
+    }
+
+    protected RoleTO updateRole(final RoleMod roleMod) {
+        return roleService.update(roleMod.getKey(), roleMod).readEntity(RoleTO.class);
+    }
+
+    protected RoleTO deleteRole(final Long id) {
+        return roleService.delete(id).readEntity(RoleTO.class);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected <T extends AbstractPolicyTO> T createPolicy(final T policy) {
+        Response response = policyService.create(policy);
+        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+            Exception ex = clientFactory.getExceptionMapper().fromResponse(response);
+            if (ex != null) {
+                throw (RuntimeException) ex;
+            }
+        }
+        return (T) getObject(response.getLocation(), PolicyService.class, policy.getClass());
+    }
+
+    protected ResourceTO createResource(final ResourceTO resourceTO) {
+        Response response = resourceService.create(resourceTO);
+        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+            Exception ex = clientFactory.getExceptionMapper().fromResponse(response);
+            if (ex != null) {
+                throw (RuntimeException) ex;
+            }
+        }
+        return getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+    }
+
+    protected Object getLdapRemoteObject(final String objectDn) {
+        return getLdapRemoteObject(null, null, objectDn);
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes", "UseOfObsoleteCollectionType" })
+    protected Object getLdapRemoteObject(final String bindDn, final String bindPwd, final String objectDn) {
+        ResourceTO ldapRes = resourceService.read(RESOURCE_NAME_LDAP);
+        final Map<String, ConnConfProperty> ldapConnConf =
+                connectorService.read(ldapRes.getConnectorId()).getConfigurationMap();
+
+        Hashtable env = new Hashtable();
+        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+        env.put(Context.PROVIDER_URL, "ldap://" + ldapConnConf.get("host").getValues().get(0)
+                + ":" + ldapConnConf.get("port").getValues().get(0) + "/");
+        env.put(Context.SECURITY_AUTHENTICATION, "simple");
+        env.put(Context.SECURITY_PRINCIPAL,
+                bindDn == null ? ldapConnConf.get("principal").getValues().get(0) : bindDn);
+        env.put(Context.SECURITY_CREDENTIALS,
+                bindPwd == null ? ldapConnConf.get("credentials").getValues().get(0) : bindPwd);
+
+        try {
+            final InitialDirContext ctx = new InitialDirContext(env);
+            return ctx.lookup(objectDn);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ActivitiDetector.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ActivitiDetector.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ActivitiDetector.java
new file mode 100644
index 0000000..ae35a23
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ActivitiDetector.java
@@ -0,0 +1,36 @@
+/*
+ * 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.server.reference;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// TODO: REMOVE!!!
+public class ActivitiDetector {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ActivitiDetector.class);
+
+    public static boolean isActivitiEnabledForUsers() {
+        return false;
+    }
+
+    public static boolean isActivitiEnabledForRoles() {
+        return false;
+    }
+}


[06/15] syncope git commit: FIT server integration tests

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractPushResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractPushResultHandler.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractPushResultHandler.java
new file mode 100644
index 0000000..74cd5f0
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractPushResultHandler.java
@@ -0,0 +1,374 @@
+/*
+ * 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.server.provisioning.java.sync;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.apache.syncope.common.lib.mod.AttrMod;
+import org.apache.syncope.common.lib.mod.MembershipMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditElements.Result;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.MatchingRule;
+import org.apache.syncope.common.lib.types.PropagationByResource;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.common.lib.types.UnmatchingRule;
+import org.apache.syncope.server.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.server.persistence.api.entity.Mapping;
+import org.apache.syncope.server.persistence.api.entity.MappingItem;
+import org.apache.syncope.server.persistence.api.entity.Subject;
+import org.apache.syncope.server.persistence.api.entity.VirAttr;
+import org.apache.syncope.server.persistence.api.entity.membership.Membership;
+import org.apache.syncope.server.persistence.api.entity.task.PushTask;
+import org.apache.syncope.server.persistence.api.entity.user.User;
+import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
+import org.apache.syncope.server.provisioning.api.sync.PushActions;
+import org.apache.syncope.server.misc.MappingUtil;
+import org.apache.syncope.server.provisioning.api.sync.SyncopePushResultHandler;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.quartz.JobExecutionException;
+import org.springframework.transaction.annotation.Transactional;
+
+public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHandler<PushTask, PushActions>
+        implements SyncopePushResultHandler {
+
+    protected abstract String getName(final Subject<?, ?, ?> subject);
+
+    protected abstract Mapping<?> getMapping();
+
+    protected abstract AbstractSubjectTO getSubjectTO(final long key);
+
+    protected abstract Subject<?, ?, ?> getSubject(final long key);
+
+    protected abstract Subject<?, ?, ?> deprovision(final Subject<?, ?, ?> sbj);
+
+    protected abstract Subject<?, ?, ?> provision(final Subject<?, ?, ?> sbj, final Boolean enabled);
+
+    protected abstract Subject<?, ?, ?> link(final Subject<?, ?, ?> sbj, final Boolean unlink);
+
+    protected abstract Subject<?, ?, ?> unassign(final Subject<?, ?, ?> sbj);
+
+    protected abstract Subject<?, ?, ?> assign(final Subject<?, ?, ?> sbj, Boolean enabled);
+
+    protected abstract ConnectorObject getRemoteObject(final String accountId);
+
+    @Transactional
+    @Override
+    public boolean handle(final long subjectId) {
+        try {
+            doHandle(subjectId);
+            return true;
+        } catch (JobExecutionException e) {
+            LOG.error("Synchronization failed", e);
+            return false;
+        }
+    }
+
+    protected final void doHandle(final long subjectId)
+            throws JobExecutionException {
+
+        final Subject<?, ?, ?> subject = getSubject(subjectId);
+
+        final AttributableUtil attrUtil = attrUtilFactory.getInstance(subject);
+
+        final ProvisioningResult result = new ProvisioningResult();
+        profile.getResults().add(result);
+
+        result.setId(subject.getKey());
+        result.setSubjectType(attrUtil.getType());
+        result.setName(getName(subject));
+
+        final Boolean enabled = subject instanceof User && profile.getTask().isSyncStatus()
+                ? ((User) subject).isSuspended() ? Boolean.FALSE : Boolean.TRUE
+                : null;
+
+        LOG.debug("Propagating {} with key {} towards {}",
+                attrUtil.getType(), subject.getKey(), profile.getTask().getResource());
+
+        Object output = null;
+        Result resultStatus = null;
+        ConnectorObject beforeObj = null;
+        String operation = null;
+
+        // Try to read remote object (user / group) BEFORE any actual operation
+        final String accountId = MappingUtil.getAccountIdValue(
+                subject, profile.getTask().getResource(), getMapping().getAccountIdItem());
+
+        beforeObj = getRemoteObject(accountId);
+
+        Boolean status = profile.getTask().isSyncStatus() ? enabled : null;
+
+        if (profile.isDryRun()) {
+            if (beforeObj == null) {
+                result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule()));
+            } else {
+                result.setOperation(getResourceOperation(profile.getTask().getMatchingRule()));
+            }
+            result.setStatus(ProvisioningResult.Status.SUCCESS);
+        } else {
+            try {
+                if (beforeObj == null) {
+                    operation = profile.getTask().getUnmatchingRule().name().toLowerCase();
+                    result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule()));
+
+                    switch (profile.getTask().getUnmatchingRule()) {
+                        case ASSIGN:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeAssign(this.getProfile(), subject);
+                            }
+
+                            if (!profile.getTask().isPerformCreate()) {
+                                LOG.debug("PushTask not configured for create");
+                            } else {
+                                assign(subject, status);
+                            }
+
+                            break;
+                        case PROVISION:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeProvision(this.getProfile(), subject);
+                            }
+
+                            if (!profile.getTask().isPerformCreate()) {
+                                LOG.debug("PushTask not configured for create");
+                            } else {
+                                provision(subject, status);
+                            }
+
+                            break;
+                        case UNLINK:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeUnlink(this.getProfile(), subject);
+                            }
+
+                            if (!profile.getTask().isPerformUpdate()) {
+                                LOG.debug("PushTask not configured for update");
+                            } else {
+                                link(subject, true);
+                            }
+
+                            break;
+                        default:
+                        // do nothing
+                    }
+
+                } else {
+                    operation = profile.getTask().getMatchingRule().name().toLowerCase();
+                    result.setOperation(getResourceOperation(profile.getTask().getMatchingRule()));
+
+                    switch (profile.getTask().getMatchingRule()) {
+                        case UPDATE:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeUpdate(this.getProfile(), subject);
+                            }
+                            if (!profile.getTask().isPerformUpdate()) {
+                                LOG.debug("PushTask not configured for update");
+                            } else {
+                                update(subject, status);
+                            }
+
+                            break;
+                        case DEPROVISION:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeDeprovision(this.getProfile(), subject);
+                            }
+
+                            if (!profile.getTask().isPerformDelete()) {
+                                LOG.debug("PushTask not configured for delete");
+                            } else {
+                                deprovision(subject);
+                            }
+
+                            break;
+                        case UNASSIGN:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeUnassign(this.getProfile(), subject);
+                            }
+
+                            if (!profile.getTask().isPerformDelete()) {
+                                LOG.debug("PushTask not configured for delete");
+                            } else {
+                                unassign(subject);
+                            }
+
+                            break;
+                        case LINK:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeLink(this.getProfile(), subject);
+                            }
+
+                            if (!profile.getTask().isPerformUpdate()) {
+                                LOG.debug("PushTask not configured for update");
+                            } else {
+                                link(subject, false);
+                            }
+
+                            break;
+                        case UNLINK:
+                            for (PushActions action : profile.getActions()) {
+                                action.beforeUnlink(this.getProfile(), subject);
+                            }
+
+                            if (!profile.getTask().isPerformUpdate()) {
+                                LOG.debug("PushTask not configured for update");
+                            } else {
+                                link(subject, true);
+                            }
+
+                            break;
+                        default:
+                        // do nothing
+                    }
+                }
+
+                for (PushActions action : profile.getActions()) {
+                    action.after(this.getProfile(), subject, result);
+                }
+
+                result.setStatus(ProvisioningResult.Status.SUCCESS);
+                resultStatus = AuditElements.Result.SUCCESS;
+                output = getRemoteObject(accountId);
+            } catch (Exception e) {
+                result.setStatus(ProvisioningResult.Status.FAILURE);
+                result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+                resultStatus = AuditElements.Result.FAILURE;
+                output = e;
+
+                LOG.warn("Error pushing {} towards {}", subject, profile.getTask().getResource(), e);
+                throw new JobExecutionException(e);
+            } finally {
+                notificationManager.createTasks(
+                        AuditElements.EventCategoryType.PUSH,
+                        AttributableType.USER.name().toLowerCase(),
+                        profile.getTask().getResource().getKey(),
+                        operation,
+                        resultStatus,
+                        beforeObj,
+                        output,
+                        subject);
+                auditManager.audit(
+                        AuditElements.EventCategoryType.PUSH,
+                        AttributableType.USER.name().toLowerCase(),
+                        profile.getTask().getResource().getKey(),
+                        operation,
+                        resultStatus,
+                        beforeObj,
+                        output,
+                        subject);
+            }
+        }
+    }
+
+    private ResourceOperation getResourceOperation(final UnmatchingRule rule) {
+        switch (rule) {
+            case ASSIGN:
+            case PROVISION:
+                return ResourceOperation.CREATE;
+            default:
+                return ResourceOperation.NONE;
+        }
+    }
+
+    private ResourceOperation getResourceOperation(final MatchingRule rule) {
+        switch (rule) {
+            case UPDATE:
+                return ResourceOperation.UPDATE;
+            case DEPROVISION:
+            case UNASSIGN:
+                return ResourceOperation.DELETE;
+            default:
+                return ResourceOperation.NONE;
+        }
+    }
+
+    protected Subject<?, ?, ?> update(final Subject<?, ?, ?> sbj, final Boolean enabled) {
+
+        final Set<MembershipMod> membsToAdd = new HashSet<>();
+        final Set<String> vattrToBeRemoved = new HashSet<>();
+        final Set<String> membVattrToBeRemoved = new HashSet<>();
+        final Set<AttrMod> vattrToBeUpdated = new HashSet<>();
+
+        // Search for all mapped vattrs
+        final Mapping<?> umapping = getMapping();
+        for (MappingItem mappingItem : umapping.getItems()) {
+            if (mappingItem.getIntMappingType() == IntMappingType.UserVirtualSchema) {
+                vattrToBeRemoved.add(mappingItem.getIntAttrName());
+            } else if (mappingItem.getIntMappingType() == IntMappingType.MembershipVirtualSchema) {
+                membVattrToBeRemoved.add(mappingItem.getIntAttrName());
+            }
+        }
+
+        // Search for all user's vattrs and:
+        // 1. add mapped vattrs not owned by the user to the set of vattrs to be removed
+        // 2. add all vattrs owned by the user to the set of vattrs to be update
+        for (VirAttr vattr : sbj.getVirAttrs()) {
+            vattrToBeRemoved.remove(vattr.getSchema().getKey());
+            final AttrMod mod = new AttrMod();
+            mod.setSchema(vattr.getSchema().getKey());
+            mod.getValuesToBeAdded().addAll(vattr.getValues());
+            vattrToBeUpdated.add(mod);
+        }
+
+        final boolean changepwd;
+
+        if (sbj instanceof User) {
+            changepwd = true;
+
+            // Search for memberships
+            for (Membership membership : User.class.cast(sbj).getMemberships()) {
+                final MembershipMod membershipMod = new MembershipMod();
+                membershipMod.setKey(membership.getKey());
+                membershipMod.setRole(membership.getRole().getKey());
+
+                for (VirAttr vattr : membership.getVirAttrs()) {
+                    membVattrToBeRemoved.remove(vattr.getSchema().getKey());
+                    final AttrMod mod = new AttrMod();
+                    mod.setSchema(vattr.getSchema().getKey());
+                    mod.getValuesToBeAdded().addAll(vattr.getValues());
+                    membershipMod.getVirAttrsToUpdate().add(mod);
+                }
+
+                membsToAdd.add(membershipMod);
+            }
+
+            if (!membsToAdd.isEmpty()) {
+                membsToAdd.iterator().next().getVirAttrsToRemove().addAll(membVattrToBeRemoved);
+            }
+        } else {
+            changepwd = false;
+        }
+
+        final List<String> noPropResources = new ArrayList<>(sbj.getResourceNames());
+        noPropResources.remove(profile.getTask().getResource().getKey());
+
+        final PropagationByResource propByRes = new PropagationByResource();
+        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
+
+        taskExecutor.execute(propagationManager.getUpdateTaskIds(
+                sbj, null, changepwd, enabled, vattrToBeRemoved, vattrToBeUpdated, propByRes, noPropResources,
+                membsToAdd));
+
+        return userDAO.authFetch(sbj.getKey());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectPushResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectPushResultHandler.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectPushResultHandler.java
deleted file mode 100644
index f0b70cd..0000000
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectPushResultHandler.java
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * 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.server.provisioning.java.sync;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import org.apache.commons.lang3.exception.ExceptionUtils;
-import org.apache.syncope.common.lib.mod.AttrMod;
-import org.apache.syncope.common.lib.mod.MembershipMod;
-import org.apache.syncope.common.lib.to.AbstractSubjectTO;
-import org.apache.syncope.common.lib.types.AttributableType;
-import org.apache.syncope.common.lib.types.AuditElements;
-import org.apache.syncope.common.lib.types.AuditElements.Result;
-import org.apache.syncope.common.lib.types.IntMappingType;
-import org.apache.syncope.common.lib.types.MatchingRule;
-import org.apache.syncope.common.lib.types.PropagationByResource;
-import org.apache.syncope.common.lib.types.ResourceOperation;
-import org.apache.syncope.common.lib.types.UnmatchingRule;
-import org.apache.syncope.server.persistence.api.entity.AttributableUtil;
-import org.apache.syncope.server.persistence.api.entity.Mapping;
-import org.apache.syncope.server.persistence.api.entity.MappingItem;
-import org.apache.syncope.server.persistence.api.entity.Subject;
-import org.apache.syncope.server.persistence.api.entity.VirAttr;
-import org.apache.syncope.server.persistence.api.entity.membership.Membership;
-import org.apache.syncope.server.persistence.api.entity.task.PushTask;
-import org.apache.syncope.server.persistence.api.entity.user.User;
-import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
-import org.apache.syncope.server.provisioning.api.sync.PushActions;
-import org.apache.syncope.server.misc.MappingUtil;
-import org.identityconnectors.framework.common.objects.ConnectorObject;
-import org.quartz.JobExecutionException;
-import org.springframework.transaction.annotation.Transactional;
-
-public abstract class AbstractSubjectPushResultHandler extends AbstractSyncopeResultHandler<PushTask, PushActions> {
-
-    protected abstract String getName(final Subject<?, ?, ?> subject);
-
-    protected abstract Mapping<?> getMapping();
-
-    protected abstract AbstractSubjectTO getSubjectTO(final long key);
-
-    protected abstract Subject<?, ?, ?> getSubject(final long key);
-
-    protected abstract Subject<?, ?, ?> deprovision(final Subject<?, ?, ?> sbj);
-
-    protected abstract Subject<?, ?, ?> provision(final Subject<?, ?, ?> sbj, final Boolean enabled);
-
-    protected abstract Subject<?, ?, ?> link(final Subject<?, ?, ?> sbj, final Boolean unlink);
-
-    protected abstract Subject<?, ?, ?> unassign(final Subject<?, ?, ?> sbj);
-
-    protected abstract Subject<?, ?, ?> assign(final Subject<?, ?, ?> sbj, Boolean enabled);
-
-    protected abstract ConnectorObject getRemoteObject(final String accountId);
-
-    @Transactional
-    public boolean handle(final long subjectId) {
-        try {
-            doHandle(subjectId);
-            return true;
-        } catch (JobExecutionException e) {
-            LOG.error("Synchronization failed", e);
-            return false;
-        }
-    }
-
-    protected final void doHandle(final long subjectId)
-            throws JobExecutionException {
-
-        final Subject<?, ?, ?> subject = getSubject(subjectId);
-
-        final AttributableUtil attrUtil = attrUtilFactory.getInstance(subject);
-
-        final ProvisioningResult result = new ProvisioningResult();
-        profile.getResults().add(result);
-
-        result.setId(subject.getKey());
-        result.setSubjectType(attrUtil.getType());
-        result.setName(getName(subject));
-
-        final Boolean enabled = subject instanceof User && profile.getTask().isSyncStatus()
-                ? ((User) subject).isSuspended() ? Boolean.FALSE : Boolean.TRUE
-                : null;
-
-        LOG.debug("Propagating {} with key {} towards {}",
-                attrUtil.getType(), subject.getKey(), profile.getTask().getResource());
-
-        Object output = null;
-        Result resultStatus = null;
-        ConnectorObject beforeObj = null;
-        String operation = null;
-
-        // Try to read remote object (user / group) BEFORE any actual operation
-        final String accountId = MappingUtil.getAccountIdValue(
-                subject, profile.getTask().getResource(), getMapping().getAccountIdItem());
-
-        beforeObj = getRemoteObject(accountId);
-
-        Boolean status = profile.getTask().isSyncStatus() ? enabled : null;
-
-        if (profile.isDryRun()) {
-            if (beforeObj == null) {
-                result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule()));
-            } else {
-                result.setOperation(getResourceOperation(profile.getTask().getMatchingRule()));
-            }
-            result.setStatus(ProvisioningResult.Status.SUCCESS);
-        } else {
-            try {
-                if (beforeObj == null) {
-                    operation = profile.getTask().getUnmatchingRule().name().toLowerCase();
-                    result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule()));
-
-                    switch (profile.getTask().getUnmatchingRule()) {
-                        case ASSIGN:
-                            for (PushActions action : profile.getActions()) {
-                                action.beforeAssign(this.getProfile(), subject);
-                            }
-
-                            if (!profile.getTask().isPerformCreate()) {
-                                LOG.debug("PushTask not configured for create");
-                            } else {
-                                assign(subject, status);
-                            }
-
-                            break;
-                        case PROVISION:
-                            for (PushActions action : profile.getActions()) {
-                                action.beforeProvision(this.getProfile(), subject);
-                            }
-
-                            if (!profile.getTask().isPerformCreate()) {
-                                LOG.debug("PushTask not configured for create");
-                            } else {
-                                provision(subject, status);
-                            }
-
-                            break;
-                        case UNLINK:
-                            for (PushActions action : profile.getActions()) {
-                                action.beforeUnlink(this.getProfile(), subject);
-                            }
-
-                            if (!profile.getTask().isPerformUpdate()) {
-                                LOG.debug("PushTask not configured for update");
-                            } else {
-                                link(subject, true);
-                            }
-
-                            break;
-                        default:
-                        // do nothing
-                    }
-
-                } else {
-                    operation = profile.getTask().getMatchingRule().name().toLowerCase();
-                    result.setOperation(getResourceOperation(profile.getTask().getMatchingRule()));
-
-                    switch (profile.getTask().getMatchingRule()) {
-                        case UPDATE:
-                            for (PushActions action : profile.getActions()) {
-                                action.beforeUpdate(this.getProfile(), subject);
-                            }
-                            if (!profile.getTask().isPerformUpdate()) {
-                                LOG.debug("PushTask not configured for update");
-                            } else {
-                                update(subject, status);
-                            }
-
-                            break;
-                        case DEPROVISION:
-                            for (PushActions action : profile.getActions()) {
-                                action.beforeDeprovision(this.getProfile(), subject);
-                            }
-
-                            if (!profile.getTask().isPerformDelete()) {
-                                LOG.debug("PushTask not configured for delete");
-                            } else {
-                                deprovision(subject);
-                            }
-
-                            break;
-                        case UNASSIGN:
-                            for (PushActions action : profile.getActions()) {
-                                action.beforeUnassign(this.getProfile(), subject);
-                            }
-
-                            if (!profile.getTask().isPerformDelete()) {
-                                LOG.debug("PushTask not configured for delete");
-                            } else {
-                                unassign(subject);
-                            }
-
-                            break;
-                        case LINK:
-                            for (PushActions action : profile.getActions()) {
-                                action.beforeLink(this.getProfile(), subject);
-                            }
-
-                            if (!profile.getTask().isPerformUpdate()) {
-                                LOG.debug("PushTask not configured for update");
-                            } else {
-                                link(subject, false);
-                            }
-
-                            break;
-                        case UNLINK:
-                            for (PushActions action : profile.getActions()) {
-                                action.beforeUnlink(this.getProfile(), subject);
-                            }
-
-                            if (!profile.getTask().isPerformUpdate()) {
-                                LOG.debug("PushTask not configured for update");
-                            } else {
-                                link(subject, true);
-                            }
-
-                            break;
-                        default:
-                        // do nothing
-                    }
-                }
-
-                for (PushActions action : profile.getActions()) {
-                    action.after(this.getProfile(), subject, result);
-                }
-
-                result.setStatus(ProvisioningResult.Status.SUCCESS);
-                resultStatus = AuditElements.Result.SUCCESS;
-                output = getRemoteObject(accountId);
-            } catch (Exception e) {
-                result.setStatus(ProvisioningResult.Status.FAILURE);
-                result.setMessage(ExceptionUtils.getRootCauseMessage(e));
-                resultStatus = AuditElements.Result.FAILURE;
-                output = e;
-
-                LOG.warn("Error pushing {} towards {}", subject, profile.getTask().getResource(), e);
-                throw new JobExecutionException(e);
-            } finally {
-                notificationManager.createTasks(
-                        AuditElements.EventCategoryType.PUSH,
-                        AttributableType.USER.name().toLowerCase(),
-                        profile.getTask().getResource().getKey(),
-                        operation,
-                        resultStatus,
-                        beforeObj,
-                        output,
-                        subject);
-                auditManager.audit(
-                        AuditElements.EventCategoryType.PUSH,
-                        AttributableType.USER.name().toLowerCase(),
-                        profile.getTask().getResource().getKey(),
-                        operation,
-                        resultStatus,
-                        beforeObj,
-                        output,
-                        subject);
-            }
-        }
-    }
-
-    private ResourceOperation getResourceOperation(final UnmatchingRule rule) {
-        switch (rule) {
-            case ASSIGN:
-            case PROVISION:
-                return ResourceOperation.CREATE;
-            default:
-                return ResourceOperation.NONE;
-        }
-    }
-
-    private ResourceOperation getResourceOperation(final MatchingRule rule) {
-        switch (rule) {
-            case UPDATE:
-                return ResourceOperation.UPDATE;
-            case DEPROVISION:
-            case UNASSIGN:
-                return ResourceOperation.DELETE;
-            default:
-                return ResourceOperation.NONE;
-        }
-    }
-
-    protected Subject<?, ?, ?> update(final Subject<?, ?, ?> sbj, final Boolean enabled) {
-
-        final Set<MembershipMod> membsToAdd = new HashSet<>();
-        final Set<String> vattrToBeRemoved = new HashSet<>();
-        final Set<String> membVattrToBeRemoved = new HashSet<>();
-        final Set<AttrMod> vattrToBeUpdated = new HashSet<>();
-
-        // Search for all mapped vattrs
-        final Mapping<?> umapping = getMapping();
-        for (MappingItem mappingItem : umapping.getItems()) {
-            if (mappingItem.getIntMappingType() == IntMappingType.UserVirtualSchema) {
-                vattrToBeRemoved.add(mappingItem.getIntAttrName());
-            } else if (mappingItem.getIntMappingType() == IntMappingType.MembershipVirtualSchema) {
-                membVattrToBeRemoved.add(mappingItem.getIntAttrName());
-            }
-        }
-
-        // Search for all user's vattrs and:
-        // 1. add mapped vattrs not owned by the user to the set of vattrs to be removed
-        // 2. add all vattrs owned by the user to the set of vattrs to be update
-        for (VirAttr vattr : sbj.getVirAttrs()) {
-            vattrToBeRemoved.remove(vattr.getSchema().getKey());
-            final AttrMod mod = new AttrMod();
-            mod.setSchema(vattr.getSchema().getKey());
-            mod.getValuesToBeAdded().addAll(vattr.getValues());
-            vattrToBeUpdated.add(mod);
-        }
-
-        final boolean changepwd;
-
-        if (sbj instanceof User) {
-            changepwd = true;
-
-            // Search for memberships
-            for (Membership membership : User.class.cast(sbj).getMemberships()) {
-                final MembershipMod membershipMod = new MembershipMod();
-                membershipMod.setKey(membership.getKey());
-                membershipMod.setRole(membership.getRole().getKey());
-
-                for (VirAttr vattr : membership.getVirAttrs()) {
-                    membVattrToBeRemoved.remove(vattr.getSchema().getKey());
-                    final AttrMod mod = new AttrMod();
-                    mod.setSchema(vattr.getSchema().getKey());
-                    mod.getValuesToBeAdded().addAll(vattr.getValues());
-                    membershipMod.getVirAttrsToUpdate().add(mod);
-                }
-
-                membsToAdd.add(membershipMod);
-            }
-
-            if (!membsToAdd.isEmpty()) {
-                membsToAdd.iterator().next().getVirAttrsToRemove().addAll(membVattrToBeRemoved);
-            }
-        } else {
-            changepwd = false;
-        }
-
-        final List<String> noPropResources = new ArrayList<>(sbj.getResourceNames());
-        noPropResources.remove(profile.getTask().getResource().getKey());
-
-        final PropagationByResource propByRes = new PropagationByResource();
-        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
-
-        taskExecutor.execute(propagationManager.getUpdateTaskIds(
-                sbj, null, changepwd, enabled, vattrToBeRemoved, vattrToBeUpdated, propByRes, noPropResources,
-                membsToAdd));
-
-        return userDAO.authFetch(sbj.getKey());
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectSyncResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectSyncResultHandler.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectSyncResultHandler.java
deleted file mode 100644
index 3c49eee..0000000
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectSyncResultHandler.java
+++ /dev/null
@@ -1,624 +0,0 @@
-/*
- * 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.server.provisioning.java.sync;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import org.apache.commons.lang3.exception.ExceptionUtils;
-import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
-import org.apache.syncope.common.lib.to.AbstractSubjectTO;
-import org.apache.syncope.common.lib.types.AuditElements;
-import org.apache.syncope.common.lib.types.AuditElements.Result;
-import org.apache.syncope.common.lib.types.ResourceOperation;
-import org.apache.syncope.server.persistence.api.dao.NotFoundException;
-import org.apache.syncope.server.persistence.api.dao.UserDAO;
-import org.apache.syncope.server.persistence.api.entity.AttributableUtil;
-import org.apache.syncope.server.persistence.api.entity.task.SyncTask;
-import org.apache.syncope.server.provisioning.api.AttributableTransformer;
-import org.apache.syncope.server.provisioning.api.propagation.PropagationException;
-import org.apache.syncope.server.provisioning.api.sync.SyncActions;
-import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
-import org.apache.syncope.server.misc.security.UnauthorizedRoleException;
-import org.identityconnectors.framework.common.objects.SyncDelta;
-import org.identityconnectors.framework.common.objects.SyncDeltaType;
-import org.identityconnectors.framework.common.objects.SyncResultsHandler;
-import org.quartz.JobExecutionException;
-import org.springframework.beans.factory.annotation.Autowired;
-
-public abstract class AbstractSubjectSyncResultHandler extends AbstractSyncopeResultHandler<SyncTask, SyncActions>
-        implements SyncResultsHandler {
-
-    @Autowired
-    protected SyncUtilities syncUtilities;
-
-    @Autowired
-    protected AttributableTransformer attrTransformer;
-
-    protected abstract AttributableUtil getAttributableUtil();
-
-    protected abstract String getName(AbstractSubjectTO subjectTO);
-
-    protected abstract AbstractSubjectTO getSubjectTO(long key);
-
-    protected abstract AbstractSubjectMod getSubjectMod(AbstractSubjectTO subjectTO, SyncDelta delta);
-
-    protected abstract AbstractSubjectTO create(AbstractSubjectTO subjectTO, SyncDelta _delta, ProvisioningResult result);
-
-    protected abstract AbstractSubjectTO link(AbstractSubjectTO before, ProvisioningResult result, boolean unlink);
-
-    protected abstract AbstractSubjectTO update(AbstractSubjectTO before, AbstractSubjectMod subjectMod,
-            SyncDelta delta, ProvisioningResult result);
-
-    protected abstract void deprovision(Long key, boolean unlink);
-
-    protected abstract void delete(Long key);
-
-    @Override
-    public boolean handle(final SyncDelta delta) {
-        try {
-            doHandle(delta, profile.getResults());
-            return true;
-        } catch (JobExecutionException e) {
-            LOG.error("Synchronization failed", e);
-            return false;
-        }
-    }
-
-    protected List<ProvisioningResult> assign(final SyncDelta delta, final AttributableUtil attrUtil)
-            throws JobExecutionException {
-        if (!profile.getTask().isPerformCreate()) {
-            LOG.debug("SyncTask not configured for create");
-            return Collections.<ProvisioningResult>emptyList();
-        }
-
-        final AbstractSubjectTO subjectTO =
-                connObjectUtil.getSubjectTO(delta.getObject(), profile.getTask(), attrUtil);
-
-        subjectTO.getResources().add(profile.getTask().getResource().getKey());
-
-        final ProvisioningResult result = new ProvisioningResult();
-        result.setOperation(ResourceOperation.CREATE);
-        result.setSubjectType(attrUtil.getType());
-        result.setStatus(ProvisioningResult.Status.SUCCESS);
-
-        // Attributable transformation (if configured)
-        AbstractSubjectTO transformed = attrTransformer.transform(subjectTO);
-        LOG.debug("Transformed: {}", transformed);
-
-        result.setName(getName(transformed));
-
-        if (profile.isDryRun()) {
-            result.setId(0L);
-        } else {
-            SyncDelta _delta = delta;
-            for (SyncActions action : profile.getActions()) {
-                _delta = action.beforeAssign(this.getProfile(), _delta, transformed);
-            }
-
-            create(transformed, _delta, attrUtil, "assign", result);
-        }
-
-        return Collections.singletonList(result);
-    }
-
-    protected List<ProvisioningResult> create(final SyncDelta delta, final AttributableUtil attrUtil)
-            throws JobExecutionException {
-
-        if (!profile.getTask().isPerformCreate()) {
-            LOG.debug("SyncTask not configured for create");
-            return Collections.<ProvisioningResult>emptyList();
-        }
-
-        final AbstractSubjectTO subjectTO =
-                connObjectUtil.getSubjectTO(delta.getObject(), profile.getTask(), attrUtil);
-
-        // Attributable transformation (if configured)
-        AbstractSubjectTO transformed = attrTransformer.transform(subjectTO);
-        LOG.debug("Transformed: {}", transformed);
-
-        final ProvisioningResult result = new ProvisioningResult();
-        result.setOperation(ResourceOperation.CREATE);
-        result.setSubjectType(attrUtil.getType());
-        result.setStatus(ProvisioningResult.Status.SUCCESS);
-
-        result.setName(getName(transformed));
-
-        if (profile.isDryRun()) {
-            result.setId(0L);
-        } else {
-            SyncDelta _delta = delta;
-            for (SyncActions action : profile.getActions()) {
-                _delta = action.beforeProvision(this.getProfile(), _delta, transformed);
-            }
-
-            create(transformed, _delta, attrUtil, "provision", result);
-        }
-
-        return Collections.<ProvisioningResult>singletonList(result);
-    }
-
-    private void create(
-            final AbstractSubjectTO subjectTO,
-            final SyncDelta delta,
-            final AttributableUtil attrUtil,
-            final String operation,
-            final ProvisioningResult result)
-            throws JobExecutionException {
-
-        Object output;
-        Result resultStatus;
-
-        try {
-            AbstractSubjectTO actual = create(subjectTO, delta, result);
-            result.setName(getName(actual));
-            output = actual;
-            resultStatus = Result.SUCCESS;
-
-            for (SyncActions action : profile.getActions()) {
-                action.after(this.getProfile(), delta, actual, result);
-            }
-        } catch (PropagationException e) {
-            // A propagation failure doesn't imply a synchronization failure.
-            // The propagation exception status will be reported into the propagation task execution.
-            LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
-            output = e;
-            resultStatus = Result.FAILURE;
-        } catch (Exception e) {
-            result.setStatus(ProvisioningResult.Status.FAILURE);
-            result.setMessage(ExceptionUtils.getRootCauseMessage(e));
-            LOG.error("Could not create {} {} ", attrUtil.getType(), delta.getUid().getUidValue(), e);
-            output = e;
-            resultStatus = Result.FAILURE;
-        }
-
-        audit(operation, resultStatus, null, output, delta);
-    }
-
-    protected List<ProvisioningResult> update(SyncDelta delta, final List<Long> subjects,
-            final AttributableUtil attrUtil)
-            throws JobExecutionException {
-
-        if (!profile.getTask().isPerformUpdate()) {
-            LOG.debug("SyncTask not configured for update");
-            return Collections.<ProvisioningResult>emptyList();
-        }
-
-        LOG.debug("About to update {}", subjects);
-
-        List<ProvisioningResult> updResults = new ArrayList<>();
-
-        for (Long id : subjects) {
-            LOG.debug("About to update {}", id);
-
-            Object output;
-            AbstractSubjectTO before = null;
-            Result resultStatus;
-
-            final ProvisioningResult result = new ProvisioningResult();
-            result.setOperation(ResourceOperation.UPDATE);
-            result.setSubjectType(attrUtil.getType());
-            result.setStatus(ProvisioningResult.Status.SUCCESS);
-            result.setId(id);
-
-            before = getSubjectTO(id);
-
-            if (before == null) {
-                result.setStatus(ProvisioningResult.Status.FAILURE);
-                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.getType().name(), id));
-            } else {
-                result.setName(getName(before));
-            }
-
-            if (!profile.isDryRun()) {
-                if (before == null) {
-                    resultStatus = Result.FAILURE;
-                    output = null;
-                } else {
-                    try {
-                        final AbstractSubjectMod attributableMod = getSubjectMod(before, delta);
-
-                        // Attribute value transformation (if configured)
-                        final AbstractSubjectMod actual = attrTransformer.transform(attributableMod);
-                        LOG.debug("Transformed: {}", actual);
-
-                        for (SyncActions action : profile.getActions()) {
-                            delta = action.beforeUpdate(this.getProfile(), delta, before, attributableMod);
-                        }
-
-                        final AbstractSubjectTO updated = update(before, attributableMod, delta, result);
-
-                        for (SyncActions action : profile.getActions()) {
-                            action.after(this.getProfile(), delta, updated, result);
-                        }
-
-                        output = updated;
-                        resultStatus = Result.SUCCESS;
-                        result.setName(getName(updated));
-                        LOG.debug("{} {} successfully updated", attrUtil.getType(), id);
-                    } catch (PropagationException e) {
-                        // A propagation failure doesn't imply a synchronization failure.
-                        // The propagation exception status will be reported into the propagation task execution.
-                        LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
-                        output = e;
-                        resultStatus = Result.FAILURE;
-                    } catch (Exception e) {
-                        result.setStatus(ProvisioningResult.Status.FAILURE);
-                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
-                        LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
-                        output = e;
-                        resultStatus = Result.FAILURE;
-                    }
-                }
-                audit("update", resultStatus, before, output, delta);
-            }
-            updResults.add(result);
-        }
-        return updResults;
-    }
-
-    protected List<ProvisioningResult> deprovision(
-            SyncDelta delta,
-            final List<Long> subjects,
-            final AttributableUtil attrUtil,
-            final boolean unlink)
-            throws JobExecutionException {
-
-        if (!profile.getTask().isPerformUpdate()) {
-            LOG.debug("SyncTask not configured for update");
-            return Collections.<ProvisioningResult>emptyList();
-        }
-
-        LOG.debug("About to update {}", subjects);
-
-        final List<ProvisioningResult> updResults = new ArrayList<>();
-
-        for (Long id : subjects) {
-            LOG.debug("About to unassign resource {}", id);
-
-            Object output;
-            Result resultStatus;
-
-            final ProvisioningResult result = new ProvisioningResult();
-            result.setOperation(ResourceOperation.DELETE);
-            result.setSubjectType(attrUtil.getType());
-            result.setStatus(ProvisioningResult.Status.SUCCESS);
-            result.setId(id);
-
-            final AbstractSubjectTO before = getSubjectTO(id);
-
-            if (before == null) {
-                result.setStatus(ProvisioningResult.Status.FAILURE);
-                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.getType().name(), id));
-            }
-
-            if (!profile.isDryRun()) {
-                if (before == null) {
-                    resultStatus = Result.FAILURE;
-                    output = null;
-                } else {
-                    result.setName(getName(before));
-
-                    try {
-                        if (unlink) {
-                            for (SyncActions action : profile.getActions()) {
-                                action.beforeUnassign(this.getProfile(), delta, before);
-                            }
-                        } else {
-                            for (SyncActions action : profile.getActions()) {
-                                action.beforeDeprovision(this.getProfile(), delta, before);
-                            }
-                        }
-
-                        deprovision(id, unlink);
-                        output = getSubjectTO(id);
-
-                        for (SyncActions action : profile.getActions()) {
-                            action.after(this.getProfile(), delta, AbstractSubjectTO.class.cast(output), result);
-                        }
-
-                        resultStatus = Result.SUCCESS;
-                        LOG.debug("{} {} successfully updated", attrUtil.getType(), id);
-                    } catch (PropagationException e) {
-                        // A propagation failure doesn't imply a synchronization failure.
-                        // The propagation exception status will be reported into the propagation task execution.
-                        LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
-                        output = e;
-                        resultStatus = Result.FAILURE;
-                    } catch (Exception e) {
-                        result.setStatus(ProvisioningResult.Status.FAILURE);
-                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
-                        LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
-                        output = e;
-                        resultStatus = Result.FAILURE;
-                    }
-                }
-                audit(unlink ? "unassign" : "deprovision", resultStatus, before, output, delta);
-            }
-            updResults.add(result);
-        }
-
-        return updResults;
-    }
-
-    protected List<ProvisioningResult> link(
-            SyncDelta delta,
-            final List<Long> subjects,
-            final AttributableUtil attrUtil,
-            final boolean unlink)
-            throws JobExecutionException {
-
-        if (!profile.getTask().isPerformUpdate()) {
-            LOG.debug("SyncTask not configured for update");
-            return Collections.<ProvisioningResult>emptyList();
-        }
-
-        LOG.debug("About to update {}", subjects);
-
-        final List<ProvisioningResult> updResults = new ArrayList<>();
-
-        for (Long id : subjects) {
-            LOG.debug("About to unassign resource {}", id);
-
-            Object output;
-            Result resultStatus;
-
-            final ProvisioningResult result = new ProvisioningResult();
-            result.setOperation(ResourceOperation.NONE);
-            result.setSubjectType(attrUtil.getType());
-            result.setStatus(ProvisioningResult.Status.SUCCESS);
-            result.setId(id);
-
-            final AbstractSubjectTO before = getSubjectTO(id);
-
-            if (before == null) {
-                result.setStatus(ProvisioningResult.Status.FAILURE);
-                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.getType().name(), id));
-            }
-
-            if (!profile.isDryRun()) {
-                if (before == null) {
-                    resultStatus = Result.FAILURE;
-                    output = null;
-                } else {
-                    result.setName(getName(before));
-
-                    try {
-                        if (unlink) {
-                            for (SyncActions action : profile.getActions()) {
-                                action.beforeUnlink(this.getProfile(), delta, before);
-                            }
-                        } else {
-                            for (SyncActions action : profile.getActions()) {
-                                action.beforeLink(this.getProfile(), delta, before);
-                            }
-                        }
-
-                        output = link(before, result, unlink);
-
-                        for (SyncActions action : profile.getActions()) {
-                            action.after(this.getProfile(), delta, AbstractSubjectTO.class.cast(output), result);
-                        }
-
-                        resultStatus = Result.SUCCESS;
-                        LOG.debug("{} {} successfully updated", attrUtil.getType(), id);
-                    } catch (PropagationException e) {
-                        // A propagation failure doesn't imply a synchronization failure.
-                        // The propagation exception status will be reported into the propagation task execution.
-                        LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
-                        output = e;
-                        resultStatus = Result.FAILURE;
-                    } catch (Exception e) {
-                        result.setStatus(ProvisioningResult.Status.FAILURE);
-                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
-                        LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
-                        output = e;
-                        resultStatus = Result.FAILURE;
-                    }
-                }
-                audit(unlink ? "unlink" : "link", resultStatus, before, output, delta);
-            }
-            updResults.add(result);
-        }
-
-        return updResults;
-    }
-
-    protected List<ProvisioningResult> delete(
-            SyncDelta delta, final List<Long> subjects, final AttributableUtil attrUtil)
-            throws JobExecutionException {
-
-        if (!profile.getTask().isPerformDelete()) {
-            LOG.debug("SyncTask not configured for delete");
-            return Collections.<ProvisioningResult>emptyList();
-        }
-
-        LOG.debug("About to delete {}", subjects);
-
-        List<ProvisioningResult> delResults = new ArrayList<>();
-
-        for (Long id : subjects) {
-            Object output;
-            Result resultStatus = Result.FAILURE;
-
-            AbstractSubjectTO before = null;
-            final ProvisioningResult result = new ProvisioningResult();
-
-            try {
-                before = getSubjectTO(id);
-
-                result.setId(id);
-                result.setName(getName(before));
-                result.setOperation(ResourceOperation.DELETE);
-                result.setSubjectType(attrUtil.getType());
-                result.setStatus(ProvisioningResult.Status.SUCCESS);
-
-                if (!profile.isDryRun()) {
-                    for (SyncActions action : profile.getActions()) {
-                        delta = action.beforeDelete(this.getProfile(), delta, before);
-                    }
-
-                    try {
-                        delete(id);
-                        output = null;
-                        resultStatus = Result.SUCCESS;
-                    } catch (Exception e) {
-                        result.setStatus(ProvisioningResult.Status.FAILURE);
-                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
-                        LOG.error("Could not delete {} {}", attrUtil.getType(), id, e);
-                        output = e;
-                    }
-
-                    for (SyncActions action : profile.getActions()) {
-                        action.after(this.getProfile(), delta, before, result);
-                    }
-
-                    audit("delete", resultStatus, before, output, delta);
-                }
-
-                delResults.add(result);
-
-            } catch (NotFoundException e) {
-                LOG.error("Could not find {} {}", attrUtil.getType(), id, e);
-            } catch (UnauthorizedRoleException e) {
-                LOG.error("Not allowed to read {} {}", attrUtil.getType(), id, e);
-            } catch (Exception e) {
-                LOG.error("Could not delete {} {}", attrUtil.getType(), id, e);
-            }
-        }
-
-        return delResults;
-    }
-
-    /**
-     * Look into SyncDelta and take necessary profile.getActions() (create / update / delete) on user(s)/role(s).
-     *
-     * @param delta returned by the underlying profile.getConnector()
-     * @throws JobExecutionException in case of synchronization failure.
-     */
-    protected final void doHandle(final SyncDelta delta, final Collection<ProvisioningResult> syncResults)
-            throws JobExecutionException {
-
-        final AttributableUtil attrUtil = getAttributableUtil();
-
-        LOG.debug("Process {} for {} as {}",
-                delta.getDeltaType(), delta.getUid().getUidValue(), delta.getObject().getObjectClass());
-
-        final String uid = delta.getPreviousUid() == null
-                ? delta.getUid().getUidValue()
-                : delta.getPreviousUid().getUidValue();
-
-        try {
-            List<Long> subjectIds = syncUtilities.findExisting(
-                    uid, delta.getObject(), profile.getTask().getResource(), attrUtil);
-
-            if (subjectIds.size() > 1) {
-                switch (profile.getResAct()) {
-                    case IGNORE:
-                        throw new IllegalStateException("More than one match " + subjectIds);
-
-                    case FIRSTMATCH:
-                        subjectIds = subjectIds.subList(0, 1);
-                        break;
-
-                    case LASTMATCH:
-                        subjectIds = subjectIds.subList(subjectIds.size() - 1, subjectIds.size());
-                        break;
-
-                    default:
-                    // keep subjectIds as is
-                }
-            }
-
-            if (SyncDeltaType.CREATE_OR_UPDATE == delta.getDeltaType()) {
-                if (subjectIds.isEmpty()) {
-                    switch (profile.getTask().getUnmatchingRule()) {
-                        case ASSIGN:
-                            profile.getResults().addAll(assign(delta, attrUtil));
-                            break;
-                        case PROVISION:
-                            profile.getResults().addAll(create(delta, attrUtil));
-                            break;
-                        default:
-                        // do nothing
-                    }
-                } else {
-                    switch (profile.getTask().getMatchingRule()) {
-                        case UPDATE:
-                            profile.getResults().addAll(update(delta, subjectIds, attrUtil));
-                            break;
-                        case DEPROVISION:
-                            profile.getResults().addAll(deprovision(delta, subjectIds, attrUtil, false));
-                            break;
-                        case UNASSIGN:
-                            profile.getResults().addAll(deprovision(delta, subjectIds, attrUtil, true));
-                            break;
-                        case LINK:
-                            profile.getResults().addAll(link(delta, subjectIds, attrUtil, false));
-                            break;
-                        case UNLINK:
-                            profile.getResults().addAll(link(delta, subjectIds, attrUtil, true));
-                            break;
-                        default:
-                        // do nothing
-                    }
-                }
-            } else if (SyncDeltaType.DELETE == delta.getDeltaType()) {
-                if (subjectIds.isEmpty()) {
-                    LOG.debug("No match found for deletion");
-                } else {
-                    profile.getResults().addAll(delete(delta, subjectIds, attrUtil));
-                }
-            }
-        } catch (IllegalStateException e) {
-            LOG.warn(e.getMessage());
-        } catch (IllegalArgumentException e) {
-            LOG.warn(e.getMessage());
-        }
-    }
-
-    private void audit(
-            final String event,
-            final Result result,
-            final Object before,
-            final Object output,
-            final Object... input) {
-
-        notificationManager.createTasks(
-                AuditElements.EventCategoryType.SYNCHRONIZATION,
-                getAttributableUtil().getType().name().toLowerCase(),
-                profile.getTask().getResource().getKey(),
-                event,
-                result,
-                before,
-                output,
-                input);
-
-        auditManager.audit(
-                AuditElements.EventCategoryType.SYNCHRONIZATION,
-                getAttributableUtil().getType().name().toLowerCase(),
-                profile.getTask().getResource().getKey(),
-                event,
-                result,
-                before,
-                output,
-                input);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSyncResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSyncResultHandler.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSyncResultHandler.java
new file mode 100644
index 0000000..83377f3
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSyncResultHandler.java
@@ -0,0 +1,617 @@
+/*
+ * 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.server.provisioning.java.sync;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditElements.Result;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.server.persistence.api.dao.NotFoundException;
+import org.apache.syncope.server.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.server.persistence.api.entity.task.SyncTask;
+import org.apache.syncope.server.provisioning.api.AttributableTransformer;
+import org.apache.syncope.server.provisioning.api.propagation.PropagationException;
+import org.apache.syncope.server.provisioning.api.sync.SyncActions;
+import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
+import org.apache.syncope.server.misc.security.UnauthorizedRoleException;
+import org.apache.syncope.server.provisioning.api.sync.SyncopeSyncResultHandler;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+import org.identityconnectors.framework.common.objects.SyncDeltaType;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public abstract class AbstractSyncResultHandler extends AbstractSyncopeResultHandler<SyncTask, SyncActions>
+        implements SyncopeSyncResultHandler {
+
+    @Autowired
+    protected SyncUtilities syncUtilities;
+
+    @Autowired
+    protected AttributableTransformer attrTransformer;
+
+    protected abstract AttributableUtil getAttributableUtil();
+
+    protected abstract String getName(AbstractSubjectTO subjectTO);
+
+    protected abstract AbstractSubjectTO getSubjectTO(long key);
+
+    protected abstract AbstractSubjectMod getSubjectMod(AbstractSubjectTO subjectTO, SyncDelta delta);
+
+    protected abstract AbstractSubjectTO create(AbstractSubjectTO subjectTO, SyncDelta _delta, ProvisioningResult result);
+
+    protected abstract AbstractSubjectTO link(AbstractSubjectTO before, ProvisioningResult result, boolean unlink);
+
+    protected abstract AbstractSubjectTO update(AbstractSubjectTO before, AbstractSubjectMod subjectMod,
+            SyncDelta delta, ProvisioningResult result);
+
+    protected abstract void deprovision(Long key, boolean unlink);
+
+    protected abstract void delete(Long key);
+
+    @Override
+    public boolean handle(final SyncDelta delta) {
+        try {
+            doHandle(delta);
+            return true;
+        } catch (JobExecutionException e) {
+            LOG.error("Synchronization failed", e);
+            return false;
+        }
+    }
+
+    protected List<ProvisioningResult> assign(final SyncDelta delta, final AttributableUtil attrUtil)
+            throws JobExecutionException {
+        if (!profile.getTask().isPerformCreate()) {
+            LOG.debug("SyncTask not configured for create");
+            return Collections.<ProvisioningResult>emptyList();
+        }
+
+        final AbstractSubjectTO subjectTO =
+                connObjectUtil.getSubjectTO(delta.getObject(), profile.getTask(), attrUtil);
+
+        subjectTO.getResources().add(profile.getTask().getResource().getKey());
+
+        final ProvisioningResult result = new ProvisioningResult();
+        result.setOperation(ResourceOperation.CREATE);
+        result.setSubjectType(attrUtil.getType());
+        result.setStatus(ProvisioningResult.Status.SUCCESS);
+
+        // Attributable transformation (if configured)
+        AbstractSubjectTO transformed = attrTransformer.transform(subjectTO);
+        LOG.debug("Transformed: {}", transformed);
+
+        result.setName(getName(transformed));
+
+        if (profile.isDryRun()) {
+            result.setId(0L);
+        } else {
+            SyncDelta _delta = delta;
+            for (SyncActions action : profile.getActions()) {
+                _delta = action.beforeAssign(this.getProfile(), _delta, transformed);
+            }
+
+            create(transformed, _delta, attrUtil, "assign", result);
+        }
+
+        return Collections.singletonList(result);
+    }
+
+    protected List<ProvisioningResult> create(final SyncDelta delta, final AttributableUtil attrUtil)
+            throws JobExecutionException {
+
+        if (!profile.getTask().isPerformCreate()) {
+            LOG.debug("SyncTask not configured for create");
+            return Collections.<ProvisioningResult>emptyList();
+        }
+
+        final AbstractSubjectTO subjectTO =
+                connObjectUtil.getSubjectTO(delta.getObject(), profile.getTask(), attrUtil);
+
+        // Attributable transformation (if configured)
+        AbstractSubjectTO transformed = attrTransformer.transform(subjectTO);
+        LOG.debug("Transformed: {}", transformed);
+
+        final ProvisioningResult result = new ProvisioningResult();
+        result.setOperation(ResourceOperation.CREATE);
+        result.setSubjectType(attrUtil.getType());
+        result.setStatus(ProvisioningResult.Status.SUCCESS);
+
+        result.setName(getName(transformed));
+
+        if (profile.isDryRun()) {
+            result.setId(0L);
+        } else {
+            SyncDelta _delta = delta;
+            for (SyncActions action : profile.getActions()) {
+                _delta = action.beforeProvision(this.getProfile(), _delta, transformed);
+            }
+
+            create(transformed, _delta, attrUtil, "provision", result);
+        }
+
+        return Collections.<ProvisioningResult>singletonList(result);
+    }
+
+    private void create(
+            final AbstractSubjectTO subjectTO,
+            final SyncDelta delta,
+            final AttributableUtil attrUtil,
+            final String operation,
+            final ProvisioningResult result)
+            throws JobExecutionException {
+
+        Object output;
+        Result resultStatus;
+
+        try {
+            AbstractSubjectTO actual = create(subjectTO, delta, result);
+            result.setName(getName(actual));
+            output = actual;
+            resultStatus = Result.SUCCESS;
+
+            for (SyncActions action : profile.getActions()) {
+                action.after(this.getProfile(), delta, actual, result);
+            }
+        } catch (PropagationException e) {
+            // A propagation failure doesn't imply a synchronization failure.
+            // The propagation exception status will be reported into the propagation task execution.
+            LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+            output = e;
+            resultStatus = Result.FAILURE;
+        } catch (Exception e) {
+            result.setStatus(ProvisioningResult.Status.FAILURE);
+            result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+            LOG.error("Could not create {} {} ", attrUtil.getType(), delta.getUid().getUidValue(), e);
+            output = e;
+            resultStatus = Result.FAILURE;
+        }
+
+        audit(operation, resultStatus, null, output, delta);
+    }
+
+    protected List<ProvisioningResult> update(SyncDelta delta, final List<Long> subjects,
+            final AttributableUtil attrUtil)
+            throws JobExecutionException {
+
+        if (!profile.getTask().isPerformUpdate()) {
+            LOG.debug("SyncTask not configured for update");
+            return Collections.<ProvisioningResult>emptyList();
+        }
+
+        LOG.debug("About to update {}", subjects);
+
+        List<ProvisioningResult> results = new ArrayList<>();
+
+        for (Long key : subjects) {
+            LOG.debug("About to update {}", key);
+
+            final ProvisioningResult result = new ProvisioningResult();
+            result.setOperation(ResourceOperation.UPDATE);
+            result.setSubjectType(attrUtil.getType());
+            result.setStatus(ProvisioningResult.Status.SUCCESS);
+            result.setId(key);
+
+            AbstractSubjectTO before = getSubjectTO(key);
+            if (before == null) {
+                result.setStatus(ProvisioningResult.Status.FAILURE);
+                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.getType().name(), key));
+            } else {
+                result.setName(getName(before));
+            }
+
+            Result resultStatus;
+            Object output;
+            if (!profile.isDryRun()) {
+                if (before == null) {
+                    resultStatus = Result.FAILURE;
+                    output = null;
+                } else {
+                    try {
+                        final AbstractSubjectMod attributableMod = getSubjectMod(before, delta);
+
+                        // Attribute value transformation (if configured)
+                        final AbstractSubjectMod actual = attrTransformer.transform(attributableMod);
+                        LOG.debug("Transformed: {}", actual);
+
+                        for (SyncActions action : profile.getActions()) {
+                            delta = action.beforeUpdate(this.getProfile(), delta, before, attributableMod);
+                        }
+
+                        final AbstractSubjectTO updated = update(before, attributableMod, delta, result);
+
+                        for (SyncActions action : profile.getActions()) {
+                            action.after(this.getProfile(), delta, updated, result);
+                        }
+
+                        output = updated;
+                        resultStatus = Result.SUCCESS;
+                        result.setName(getName(updated));
+                        LOG.debug("{} {} successfully updated", attrUtil.getType(), key);
+                    } catch (PropagationException e) {
+                        // A propagation failure doesn't imply a synchronization failure.
+                        // The propagation exception status will be reported into the propagation task execution.
+                        LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+                        output = e;
+                        resultStatus = Result.FAILURE;
+                    } catch (Exception e) {
+                        result.setStatus(ProvisioningResult.Status.FAILURE);
+                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+                        LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+                        output = e;
+                        resultStatus = Result.FAILURE;
+                    }
+                }
+                audit("update", resultStatus, before, output, delta);
+            }
+            results.add(result);
+        }
+        return results;
+    }
+
+    protected List<ProvisioningResult> deprovision(
+            SyncDelta delta,
+            final List<Long> subjects,
+            final AttributableUtil attrUtil,
+            final boolean unlink)
+            throws JobExecutionException {
+
+        if (!profile.getTask().isPerformUpdate()) {
+            LOG.debug("SyncTask not configured for update");
+            return Collections.<ProvisioningResult>emptyList();
+        }
+
+        LOG.debug("About to update {}", subjects);
+
+        final List<ProvisioningResult> updResults = new ArrayList<>();
+
+        for (Long id : subjects) {
+            LOG.debug("About to unassign resource {}", id);
+
+            Object output;
+            Result resultStatus;
+
+            final ProvisioningResult result = new ProvisioningResult();
+            result.setOperation(ResourceOperation.DELETE);
+            result.setSubjectType(attrUtil.getType());
+            result.setStatus(ProvisioningResult.Status.SUCCESS);
+            result.setId(id);
+
+            final AbstractSubjectTO before = getSubjectTO(id);
+
+            if (before == null) {
+                result.setStatus(ProvisioningResult.Status.FAILURE);
+                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.getType().name(), id));
+            }
+
+            if (!profile.isDryRun()) {
+                if (before == null) {
+                    resultStatus = Result.FAILURE;
+                    output = null;
+                } else {
+                    result.setName(getName(before));
+
+                    try {
+                        if (unlink) {
+                            for (SyncActions action : profile.getActions()) {
+                                action.beforeUnassign(this.getProfile(), delta, before);
+                            }
+                        } else {
+                            for (SyncActions action : profile.getActions()) {
+                                action.beforeDeprovision(this.getProfile(), delta, before);
+                            }
+                        }
+
+                        deprovision(id, unlink);
+                        output = getSubjectTO(id);
+
+                        for (SyncActions action : profile.getActions()) {
+                            action.after(this.getProfile(), delta, AbstractSubjectTO.class.cast(output), result);
+                        }
+
+                        resultStatus = Result.SUCCESS;
+                        LOG.debug("{} {} successfully updated", attrUtil.getType(), id);
+                    } catch (PropagationException e) {
+                        // A propagation failure doesn't imply a synchronization failure.
+                        // The propagation exception status will be reported into the propagation task execution.
+                        LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+                        output = e;
+                        resultStatus = Result.FAILURE;
+                    } catch (Exception e) {
+                        result.setStatus(ProvisioningResult.Status.FAILURE);
+                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+                        LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+                        output = e;
+                        resultStatus = Result.FAILURE;
+                    }
+                }
+                audit(unlink ? "unassign" : "deprovision", resultStatus, before, output, delta);
+            }
+            updResults.add(result);
+        }
+
+        return updResults;
+    }
+
+    protected List<ProvisioningResult> link(
+            SyncDelta delta,
+            final List<Long> subjects,
+            final AttributableUtil attrUtil,
+            final boolean unlink)
+            throws JobExecutionException {
+
+        if (!profile.getTask().isPerformUpdate()) {
+            LOG.debug("SyncTask not configured for update");
+            return Collections.<ProvisioningResult>emptyList();
+        }
+
+        LOG.debug("About to update {}", subjects);
+
+        final List<ProvisioningResult> updResults = new ArrayList<>();
+
+        for (Long id : subjects) {
+            LOG.debug("About to unassign resource {}", id);
+
+            Object output;
+            Result resultStatus;
+
+            final ProvisioningResult result = new ProvisioningResult();
+            result.setOperation(ResourceOperation.NONE);
+            result.setSubjectType(attrUtil.getType());
+            result.setStatus(ProvisioningResult.Status.SUCCESS);
+            result.setId(id);
+
+            final AbstractSubjectTO before = getSubjectTO(id);
+
+            if (before == null) {
+                result.setStatus(ProvisioningResult.Status.FAILURE);
+                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.getType().name(), id));
+            }
+
+            if (!profile.isDryRun()) {
+                if (before == null) {
+                    resultStatus = Result.FAILURE;
+                    output = null;
+                } else {
+                    result.setName(getName(before));
+
+                    try {
+                        if (unlink) {
+                            for (SyncActions action : profile.getActions()) {
+                                action.beforeUnlink(this.getProfile(), delta, before);
+                            }
+                        } else {
+                            for (SyncActions action : profile.getActions()) {
+                                action.beforeLink(this.getProfile(), delta, before);
+                            }
+                        }
+
+                        output = link(before, result, unlink);
+
+                        for (SyncActions action : profile.getActions()) {
+                            action.after(this.getProfile(), delta, AbstractSubjectTO.class.cast(output), result);
+                        }
+
+                        resultStatus = Result.SUCCESS;
+                        LOG.debug("{} {} successfully updated", attrUtil.getType(), id);
+                    } catch (PropagationException e) {
+                        // A propagation failure doesn't imply a synchronization failure.
+                        // The propagation exception status will be reported into the propagation task execution.
+                        LOG.error("Could not propagate {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+                        output = e;
+                        resultStatus = Result.FAILURE;
+                    } catch (Exception e) {
+                        result.setStatus(ProvisioningResult.Status.FAILURE);
+                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+                        LOG.error("Could not update {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+                        output = e;
+                        resultStatus = Result.FAILURE;
+                    }
+                }
+                audit(unlink ? "unlink" : "link", resultStatus, before, output, delta);
+            }
+            updResults.add(result);
+        }
+
+        return updResults;
+    }
+
+    protected List<ProvisioningResult> delete(
+            SyncDelta delta, final List<Long> subjects, final AttributableUtil attrUtil)
+            throws JobExecutionException {
+
+        if (!profile.getTask().isPerformDelete()) {
+            LOG.debug("SyncTask not configured for delete");
+            return Collections.<ProvisioningResult>emptyList();
+        }
+
+        LOG.debug("About to delete {}", subjects);
+
+        List<ProvisioningResult> delResults = new ArrayList<>();
+
+        for (Long id : subjects) {
+            Object output;
+            Result resultStatus = Result.FAILURE;
+
+            AbstractSubjectTO before = null;
+            final ProvisioningResult result = new ProvisioningResult();
+
+            try {
+                before = getSubjectTO(id);
+
+                result.setId(id);
+                result.setName(getName(before));
+                result.setOperation(ResourceOperation.DELETE);
+                result.setSubjectType(attrUtil.getType());
+                result.setStatus(ProvisioningResult.Status.SUCCESS);
+
+                if (!profile.isDryRun()) {
+                    for (SyncActions action : profile.getActions()) {
+                        delta = action.beforeDelete(this.getProfile(), delta, before);
+                    }
+
+                    try {
+                        delete(id);
+                        output = null;
+                        resultStatus = Result.SUCCESS;
+                    } catch (Exception e) {
+                        result.setStatus(ProvisioningResult.Status.FAILURE);
+                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+                        LOG.error("Could not delete {} {}", attrUtil.getType(), id, e);
+                        output = e;
+                    }
+
+                    for (SyncActions action : profile.getActions()) {
+                        action.after(this.getProfile(), delta, before, result);
+                    }
+
+                    audit("delete", resultStatus, before, output, delta);
+                }
+
+                delResults.add(result);
+
+            } catch (NotFoundException e) {
+                LOG.error("Could not find {} {}", attrUtil.getType(), id, e);
+            } catch (UnauthorizedRoleException e) {
+                LOG.error("Not allowed to read {} {}", attrUtil.getType(), id, e);
+            } catch (Exception e) {
+                LOG.error("Could not delete {} {}", attrUtil.getType(), id, e);
+            }
+        }
+
+        return delResults;
+    }
+
+    /**
+     * Look into SyncDelta and take necessary profile.getActions() (create / update / delete) on user(s)/role(s).
+     *
+     * @param delta returned by the underlying profile.getConnector()
+     * @throws JobExecutionException in case of synchronization failure.
+     */
+    protected final void doHandle(final SyncDelta delta)
+            throws JobExecutionException {
+
+        final AttributableUtil attrUtil = getAttributableUtil();
+
+        LOG.debug("Process {} for {} as {}",
+                delta.getDeltaType(), delta.getUid().getUidValue(), delta.getObject().getObjectClass());
+
+        final String uid = delta.getPreviousUid() == null
+                ? delta.getUid().getUidValue()
+                : delta.getPreviousUid().getUidValue();
+
+        try {
+            List<Long> subjectKeys = syncUtilities.findExisting(
+                    uid, delta.getObject(), profile.getTask().getResource(), attrUtil);
+
+            if (subjectKeys.size() > 1) {
+                switch (profile.getResAct()) {
+                    case IGNORE:
+                        throw new IllegalStateException("More than one match " + subjectKeys);
+
+                    case FIRSTMATCH:
+                        subjectKeys = subjectKeys.subList(0, 1);
+                        break;
+
+                    case LASTMATCH:
+                        subjectKeys = subjectKeys.subList(subjectKeys.size() - 1, subjectKeys.size());
+                        break;
+
+                    default:
+                    // keep subjectIds as is
+                }
+            }
+
+            if (SyncDeltaType.CREATE_OR_UPDATE == delta.getDeltaType()) {
+                if (subjectKeys.isEmpty()) {
+                    switch (profile.getTask().getUnmatchingRule()) {
+                        case ASSIGN:
+                            profile.getResults().addAll(assign(delta, attrUtil));
+                            break;
+                        case PROVISION:
+                            profile.getResults().addAll(create(delta, attrUtil));
+                            break;
+                        default:
+                        // do nothing
+                    }
+                } else {
+                    switch (profile.getTask().getMatchingRule()) {
+                        case UPDATE:
+                            profile.getResults().addAll(update(delta, subjectKeys, attrUtil));
+                            break;
+                        case DEPROVISION:
+                            profile.getResults().addAll(deprovision(delta, subjectKeys, attrUtil, false));
+                            break;
+                        case UNASSIGN:
+                            profile.getResults().addAll(deprovision(delta, subjectKeys, attrUtil, true));
+                            break;
+                        case LINK:
+                            profile.getResults().addAll(link(delta, subjectKeys, attrUtil, false));
+                            break;
+                        case UNLINK:
+                            profile.getResults().addAll(link(delta, subjectKeys, attrUtil, true));
+                            break;
+                        default:
+                        // do nothing
+                    }
+                }
+            } else if (SyncDeltaType.DELETE == delta.getDeltaType()) {
+                if (subjectKeys.isEmpty()) {
+                    LOG.debug("No match found for deletion");
+                } else {
+                    profile.getResults().addAll(delete(delta, subjectKeys, attrUtil));
+                }
+            }
+        } catch (IllegalStateException | IllegalArgumentException e) {
+            LOG.warn(e.getMessage());
+        }
+    }
+
+    private void audit(
+            final String event,
+            final Result result,
+            final Object before,
+            final Object output,
+            final Object... input) {
+
+        notificationManager.createTasks(
+                AuditElements.EventCategoryType.SYNCHRONIZATION,
+                getAttributableUtil().getType().name().toLowerCase(),
+                profile.getTask().getResource().getKey(),
+                event,
+                result,
+                before,
+                output,
+                input);
+
+        auditManager.audit(
+                AuditElements.EventCategoryType.SYNCHRONIZATION,
+                getAttributableUtil().getType().name().toLowerCase(),
+                profile.getTask().getResource().getKey(),
+                event,
+                result,
+                before,
+                output,
+                input);
+    }
+}


[05/15] syncope git commit: FIT server integration tests

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/DefaultPushActions.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/DefaultPushActions.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/DefaultPushActions.java
index a9ba96e..dbbdd2a 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/DefaultPushActions.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/DefaultPushActions.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.server.provisioning.java.sync;
 
-import java.util.List;
 import org.apache.syncope.server.persistence.api.entity.Subject;
 import org.apache.syncope.server.provisioning.api.sync.PushActions;
 import org.apache.syncope.server.provisioning.api.sync.ProvisioningProfile;
@@ -78,11 +77,12 @@ public abstract class DefaultPushActions implements PushActions {
 
     @Override
     public <T extends Subject<?, ?, ?>> void after(
-            final ProvisioningProfile<?, ?> profile, final T subject, final ProvisioningResult result) throws JobExecutionException {
+            final ProvisioningProfile<?, ?> profile, final T subject, final ProvisioningResult result)
+            throws JobExecutionException {
     }
 
     @Override
-    public void afterAll(final ProvisioningProfile<?, ?> profile, final List<ProvisioningResult> results)
+    public void afterAll(final ProvisioningProfile<?, ?> profile)
             throws JobExecutionException {
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/DefaultSyncActions.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/DefaultSyncActions.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/DefaultSyncActions.java
index b957d2a..6589cdf 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/DefaultSyncActions.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/DefaultSyncActions.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.server.provisioning.java.sync;
 
-import java.util.List;
 import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
 import org.apache.syncope.common.lib.to.AbstractSubjectTO;
 import org.apache.syncope.server.provisioning.api.sync.SyncActions;
@@ -104,12 +103,13 @@ public abstract class DefaultSyncActions implements SyncActions {
 
     @Override
     public <T extends AbstractSubjectTO> void after(
-            final ProvisioningProfile<?, ?> profile, final SyncDelta delta, final T subject, final ProvisioningResult result)
+            final ProvisioningProfile<?, ?> profile, final SyncDelta delta, final T subject,
+            final ProvisioningResult result)
             throws JobExecutionException {
     }
 
     @Override
-    public void afterAll(final ProvisioningProfile<?, ?> profile, final List<ProvisioningResult> results)
+    public void afterAll(final ProvisioningProfile<?, ?> profile)
             throws JobExecutionException {
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/LDAPMembershipSyncActions.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/LDAPMembershipSyncActions.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/LDAPMembershipSyncActions.java
index b705c64..145cd4d 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/LDAPMembershipSyncActions.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/LDAPMembershipSyncActions.java
@@ -146,17 +146,17 @@ public class LDAPMembershipSyncActions extends DefaultSyncActions {
     /**
      * Build UserMod for adding membership to given user, for given role.
      *
-     * @param userId user to be assigned membership to given role
+     * @param userKey user to be assigned membership to given role
      * @param roleTO role for adding membership
      * @return UserMod for user update
      */
-    protected UserMod getUserMod(final Long userId, final RoleTO roleTO) {
+    protected UserMod getUserMod(final Long userKey, final RoleTO roleTO) {
         UserMod userMod = new UserMod();
         // no actual modification takes place when user has already the role assigned
-        if (membersBeforeRoleUpdate.containsKey(userId)) {
-            membersBeforeRoleUpdate.remove(userId);
+        if (membersBeforeRoleUpdate.containsKey(userKey)) {
+            membersBeforeRoleUpdate.remove(userKey);
         } else {
-            userMod.setKey(userId);
+            userMod.setKey(userKey);
 
             MembershipMod membershipMod = new MembershipMod();
             membershipMod.setRole(roleTO.getKey());
@@ -265,13 +265,13 @@ public class LDAPMembershipSyncActions extends DefaultSyncActions {
         final Connector connector = profile.getConnector();
 
         for (Object membValue : getMembAttrValues(delta, connector)) {
-            Long userId = syncUtilities.findMatchingAttributableId(
+            Long userKey = syncUtilities.findMatchingAttributableKey(
                     ObjectClass.ACCOUNT,
                     membValue.toString(),
                     profile.getTask().getResource(),
                     profile.getConnector());
-            if (userId != null) {
-                UserMod userMod = getUserMod(userId, roleTO);
+            if (userKey != null) {
+                UserMod userMod = getUserMod(userKey, roleTO);
                 userUpdate(userMod, resource.getKey());
             }
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/PushJobImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/PushJobImpl.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/PushJobImpl.java
index d40b4be..3f7b291 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/PushJobImpl.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/PushJobImpl.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.server.provisioning.java.sync;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
@@ -36,11 +35,12 @@ import org.apache.syncope.server.persistence.api.entity.user.UMapping;
 import org.apache.syncope.server.persistence.api.entity.user.User;
 import org.apache.syncope.server.provisioning.api.Connector;
 import org.apache.syncope.server.provisioning.api.sync.ProvisioningProfile;
-import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
 import org.apache.syncope.server.provisioning.api.sync.PushActions;
 import org.apache.syncope.server.misc.spring.ApplicationContextProvider;
 import org.apache.syncope.server.misc.search.SearchCondConverter;
 import org.apache.syncope.server.provisioning.api.job.PushJob;
+import org.apache.syncope.server.provisioning.api.sync.RolePushResultHandler;
+import org.apache.syncope.server.provisioning.api.sync.UserPushResultHandler;
 import org.quartz.JobExecutionException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
@@ -50,6 +50,7 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
  *
  * @see AbstractProvisioningJob
  * @see PushTask
+ * @see PushActions
  */
 public class PushJobImpl extends AbstractProvisioningJob<PushTask, PushActions> implements PushJob {
 
@@ -82,27 +83,26 @@ public class PushJobImpl extends AbstractProvisioningJob<PushTask, PushActions>
             final boolean dryRun) throws JobExecutionException {
         LOG.debug("Execute synchronization (push) with resource {}", pushTask.getResource());
 
-        final List<ProvisioningResult> results = new ArrayList<>();
-
         final Set<Long> authorizations = RoleEntitlementUtil.getRoleKeys(entitlementDAO.findAll());
 
         final ProvisioningProfile<PushTask, PushActions> profile = new ProvisioningProfile<>(connector, pushTask);
-        profile.getActions().addAll(actions);
+        if (actions != null) {
+            profile.getActions().addAll(actions);
+        }
         profile.setDryRun(dryRun);
         profile.setResAct(null);
-        profile.getResults().addAll(results);
 
         final UserPushResultHandler uhandler =
-                (UserPushResultHandler) ApplicationContextProvider.getApplicationContext().getBeanFactory().createBean(
-                        UserPushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+                (UserPushResultHandler) ApplicationContextProvider.getApplicationContext().getBeanFactory().
+                createBean(UserPushResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
         uhandler.setProfile(profile);
 
         final RolePushResultHandler rhandler =
-                (RolePushResultHandler) ApplicationContextProvider.getApplicationContext().getBeanFactory().createBean(
-                        RolePushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+                (RolePushResultHandler) ApplicationContextProvider.getApplicationContext().getBeanFactory().
+                createBean(RolePushResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
         rhandler.setProfile(profile);
 
-        if (!profile.isDryRun()) {
+        if (actions != null && !profile.isDryRun()) {
             for (PushActions action : actions) {
                 action.beforeAll(profile);
             }
@@ -139,13 +139,13 @@ public class PushJobImpl extends AbstractProvisioningJob<PushTask, PushActions>
             }
         }
 
-        if (!profile.isDryRun()) {
+        if (actions != null && !profile.isDryRun()) {
             for (PushActions action : actions) {
-                action.afterAll(profile, results);
+                action.afterAll(profile);
             }
         }
 
-        final String result = createReport(results, pushTask.getResource().getSyncTraceLevel(), dryRun);
+        final String result = createReport(profile.getResults(), pushTask.getResource().getSyncTraceLevel(), dryRun);
 
         LOG.debug("Sync result: {}", result);
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/RolePushResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/RolePushResultHandler.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/RolePushResultHandler.java
deleted file mode 100644
index 60d6bdb..0000000
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/RolePushResultHandler.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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.server.provisioning.java.sync;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import org.apache.syncope.common.lib.mod.RoleMod;
-import org.apache.syncope.common.lib.to.AbstractSubjectTO;
-import org.apache.syncope.common.lib.to.RoleTO;
-import org.apache.syncope.common.lib.types.PropagationByResource;
-import org.apache.syncope.common.lib.types.ResourceOperation;
-import org.apache.syncope.server.persistence.api.entity.Mapping;
-import org.apache.syncope.server.persistence.api.entity.MappingItem;
-import org.apache.syncope.server.persistence.api.entity.Subject;
-import org.apache.syncope.server.persistence.api.entity.role.Role;
-import org.apache.syncope.server.provisioning.api.TimeoutException;
-import org.identityconnectors.framework.common.objects.ConnectorObject;
-import org.identityconnectors.framework.common.objects.ObjectClass;
-import org.identityconnectors.framework.common.objects.Uid;
-
-public class RolePushResultHandler extends AbstractSubjectPushResultHandler {
-
-    @Override
-    protected Subject<?, ?, ?> deprovision(final Subject<?, ?, ?> sbj) {
-        final RoleTO before = roleTransfer.getRoleTO(Role.class.cast(sbj));
-
-        final List<String> noPropResources = new ArrayList<>(before.getResources());
-        noPropResources.remove(profile.getTask().getResource().getKey());
-
-        taskExecutor.execute(propagationManager.getRoleDeleteTaskIds(before.getKey(), noPropResources));
-
-        return roleDAO.authFetch(before.getKey());
-    }
-
-    @Override
-    protected Subject<?, ?, ?> provision(final Subject<?, ?, ?> sbj, final Boolean enabled) {
-        final RoleTO before = roleTransfer.getRoleTO(Role.class.cast(sbj));
-
-        final List<String> noPropResources = new ArrayList<>(before.getResources());
-        noPropResources.remove(profile.getTask().getResource().getKey());
-
-        final PropagationByResource propByRes = new PropagationByResource();
-        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
-
-        taskExecutor.execute(propagationManager.getRoleCreateTaskIds(
-                before.getKey(),
-                Collections.unmodifiableCollection(before.getVirAttrs()),
-                propByRes,
-                noPropResources));
-
-        return roleDAO.authFetch(before.getKey());
-    }
-
-    @Override
-    protected Subject<?, ?, ?> link(final Subject<?, ?, ?> sbj, final Boolean unlink) {
-        final RoleMod roleMod = new RoleMod();
-        roleMod.setKey(sbj.getKey());
-
-        if (unlink) {
-            roleMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
-        } else {
-            roleMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
-        }
-
-        rwfAdapter.update(roleMod);
-
-        return roleDAO.authFetch(sbj.getKey());
-    }
-
-    @Override
-    protected Subject<?, ?, ?> unassign(final Subject<?, ?, ?> sbj) {
-        final RoleMod roleMod = new RoleMod();
-        roleMod.setKey(sbj.getKey());
-        roleMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
-        rwfAdapter.update(roleMod);
-        return deprovision(sbj);
-    }
-
-    @Override
-    protected Subject<?, ?, ?> assign(final Subject<?, ?, ?> sbj, final Boolean enabled) {
-        final RoleMod roleMod = new RoleMod();
-        roleMod.setKey(sbj.getKey());
-        roleMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
-        rwfAdapter.update(roleMod);
-        return provision(sbj, enabled);
-    }
-
-    @Override
-    protected String getName(final Subject<?, ?, ?> subject) {
-        return Role.class.cast(subject).getName();
-    }
-
-    @Override
-    protected AbstractSubjectTO getSubjectTO(final long key) {
-        try {
-            return roleTransfer.getRoleTO(roleDAO.authFetch(key));
-        } catch (Exception e) {
-            LOG.warn("Error retrieving user {}", key, e);
-            return null;
-        }
-    }
-
-    @Override
-    protected Subject<?, ?, ?> getSubject(final long key) {
-        try {
-            return roleDAO.authFetch(key);
-        } catch (Exception e) {
-            LOG.warn("Error retrieving role {}", key, e);
-            return null;
-        }
-    }
-
-    @Override
-    protected ConnectorObject getRemoteObject(final String accountId) {
-        ConnectorObject obj = null;
-
-        try {
-            final Uid uid = new Uid(accountId);
-
-            obj = profile.getConnector().getObject(
-                    ObjectClass.GROUP,
-                    uid,
-                    profile.getConnector().getOperationOptions(Collections.<MappingItem>emptySet()));
-        } catch (TimeoutException toe) {
-            LOG.debug("Request timeout", toe);
-            throw toe;
-        } catch (RuntimeException ignore) {
-            LOG.debug("While resolving {}", accountId, ignore);
-        }
-        return obj;
-    }
-
-    @Override
-    protected Mapping<?> getMapping() {
-        return profile.getTask().getResource().getRmapping();
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/RolePushResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/RolePushResultHandlerImpl.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/RolePushResultHandlerImpl.java
new file mode 100644
index 0000000..112d8cc
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/RolePushResultHandlerImpl.java
@@ -0,0 +1,155 @@
+/*
+ * 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.server.provisioning.java.sync;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.apache.syncope.common.lib.mod.RoleMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.types.PropagationByResource;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.server.persistence.api.entity.Mapping;
+import org.apache.syncope.server.persistence.api.entity.MappingItem;
+import org.apache.syncope.server.persistence.api.entity.Subject;
+import org.apache.syncope.server.persistence.api.entity.role.Role;
+import org.apache.syncope.server.provisioning.api.TimeoutException;
+import org.apache.syncope.server.provisioning.api.sync.RolePushResultHandler;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.Uid;
+
+public class RolePushResultHandlerImpl extends AbstractPushResultHandler implements RolePushResultHandler {
+
+    @Override
+    protected Subject<?, ?, ?> deprovision(final Subject<?, ?, ?> sbj) {
+        final RoleTO before = roleTransfer.getRoleTO(Role.class.cast(sbj));
+
+        final List<String> noPropResources = new ArrayList<>(before.getResources());
+        noPropResources.remove(profile.getTask().getResource().getKey());
+
+        taskExecutor.execute(propagationManager.getRoleDeleteTaskIds(before.getKey(), noPropResources));
+
+        return roleDAO.authFetch(before.getKey());
+    }
+
+    @Override
+    protected Subject<?, ?, ?> provision(final Subject<?, ?, ?> sbj, final Boolean enabled) {
+        final RoleTO before = roleTransfer.getRoleTO(Role.class.cast(sbj));
+
+        final List<String> noPropResources = new ArrayList<>(before.getResources());
+        noPropResources.remove(profile.getTask().getResource().getKey());
+
+        final PropagationByResource propByRes = new PropagationByResource();
+        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
+
+        taskExecutor.execute(propagationManager.getRoleCreateTaskIds(
+                before.getKey(),
+                Collections.unmodifiableCollection(before.getVirAttrs()),
+                propByRes,
+                noPropResources));
+
+        return roleDAO.authFetch(before.getKey());
+    }
+
+    @Override
+    protected Subject<?, ?, ?> link(final Subject<?, ?, ?> sbj, final Boolean unlink) {
+        final RoleMod roleMod = new RoleMod();
+        roleMod.setKey(sbj.getKey());
+
+        if (unlink) {
+            roleMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+        } else {
+            roleMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
+        }
+
+        rwfAdapter.update(roleMod);
+
+        return roleDAO.authFetch(sbj.getKey());
+    }
+
+    @Override
+    protected Subject<?, ?, ?> unassign(final Subject<?, ?, ?> sbj) {
+        final RoleMod roleMod = new RoleMod();
+        roleMod.setKey(sbj.getKey());
+        roleMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+        rwfAdapter.update(roleMod);
+        return deprovision(sbj);
+    }
+
+    @Override
+    protected Subject<?, ?, ?> assign(final Subject<?, ?, ?> sbj, final Boolean enabled) {
+        final RoleMod roleMod = new RoleMod();
+        roleMod.setKey(sbj.getKey());
+        roleMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
+        rwfAdapter.update(roleMod);
+        return provision(sbj, enabled);
+    }
+
+    @Override
+    protected String getName(final Subject<?, ?, ?> subject) {
+        return Role.class.cast(subject).getName();
+    }
+
+    @Override
+    protected AbstractSubjectTO getSubjectTO(final long key) {
+        try {
+            return roleTransfer.getRoleTO(key);
+        } catch (Exception e) {
+            LOG.warn("Error retrieving user {}", key, e);
+            return null;
+        }
+    }
+
+    @Override
+    protected Subject<?, ?, ?> getSubject(final long key) {
+        try {
+            return roleDAO.authFetch(key);
+        } catch (Exception e) {
+            LOG.warn("Error retrieving role {}", key, e);
+            return null;
+        }
+    }
+
+    @Override
+    protected ConnectorObject getRemoteObject(final String accountId) {
+        ConnectorObject obj = null;
+
+        try {
+            final Uid uid = new Uid(accountId);
+
+            obj = profile.getConnector().getObject(
+                    ObjectClass.GROUP,
+                    uid,
+                    profile.getConnector().getOperationOptions(Collections.<MappingItem>emptySet()));
+        } catch (TimeoutException toe) {
+            LOG.debug("Request timeout", toe);
+            throw toe;
+        } catch (RuntimeException ignore) {
+            LOG.debug("While resolving {}", accountId, ignore);
+        }
+        return obj;
+    }
+
+    @Override
+    protected Mapping<?> getMapping() {
+        return profile.getTask().getResource().getRmapping();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/RoleSyncResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/RoleSyncResultHandler.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/RoleSyncResultHandler.java
deleted file mode 100644
index d172077..0000000
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/RoleSyncResultHandler.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * 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.server.provisioning.java.sync;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
-import org.apache.syncope.common.lib.mod.AttrMod;
-import org.apache.syncope.common.lib.mod.RoleMod;
-import org.apache.syncope.common.lib.mod.UserMod;
-import org.apache.syncope.common.lib.to.AbstractSubjectTO;
-import org.apache.syncope.common.lib.to.PropagationStatus;
-import org.apache.syncope.common.lib.to.RoleTO;
-import org.apache.syncope.common.lib.types.AttributableType;
-import org.apache.syncope.server.persistence.api.entity.AttributableUtil;
-import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
-import org.identityconnectors.framework.common.objects.SyncDelta;
-
-public class RoleSyncResultHandler extends AbstractSubjectSyncResultHandler {
-
-    protected Map<Long, String> roleOwnerMap = new HashMap<>();
-
-    public Map<Long, String> getRoleOwnerMap() {
-        return this.roleOwnerMap;
-    }
-
-    @Override
-    protected AttributableUtil getAttributableUtil() {
-        return attrUtilFactory.getInstance(AttributableType.ROLE);
-    }
-
-    @Override
-    protected String getName(final AbstractSubjectTO subjectTO) {
-        return RoleTO.class.cast(subjectTO).getName();
-    }
-
-    @Override
-    protected AbstractSubjectTO getSubjectTO(final long key) {
-        try {
-            return roleTransfer.getRoleTO(roleDAO.authFetch(key));
-        } catch (Exception e) {
-            LOG.warn("Error retrieving role {}", key, e);
-            return null;
-        }
-    }
-
-    @Override
-    protected AbstractSubjectMod getSubjectMod(
-            final AbstractSubjectTO subjectTO, final SyncDelta delta) {
-
-        return connObjectUtil.getAttributableMod(
-                subjectTO.getKey(),
-                delta.getObject(),
-                subjectTO,
-                profile.getTask(),
-                attrUtilFactory.getInstance(AttributableType.ROLE));
-    }
-
-    @Override
-    protected AbstractSubjectTO create(
-            final AbstractSubjectTO subjectTO, final SyncDelta _delta, final ProvisioningResult result) {
-
-        RoleTO roleTO = RoleTO.class.cast(subjectTO);
-
-        Map.Entry<Long, List<PropagationStatus>> created = roleProvisioningManager.create(roleTO, roleOwnerMap,
-                Collections.singleton(profile.getTask().getResource().getKey()));
-
-        roleTO = roleTransfer.getRoleTO(roleDAO.authFetch(created.getKey()));
-
-        result.setId(created.getKey());
-        result.setName(getName(subjectTO));
-
-        return roleTO;
-    }
-
-    @Override
-    protected AbstractSubjectTO link(
-            final AbstractSubjectTO before,
-            final ProvisioningResult result,
-            final boolean unlink) {
-
-        final RoleMod roleMod = new RoleMod();
-        roleMod.setKey(before.getKey());
-
-        if (unlink) {
-            roleMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
-        } else {
-            roleMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
-        }
-
-        return roleTransfer.getRoleTO(roleDAO.authFetch(rwfAdapter.update(roleMod).getResult()));
-    }
-
-    @Override
-    protected AbstractSubjectTO update(
-            final AbstractSubjectTO before,
-            final AbstractSubjectMod subjectMod,
-            final SyncDelta delta,
-            final ProvisioningResult result) {
-
-        RoleMod roleMod = RoleMod.class.cast(subjectMod);
-
-        Map.Entry<Long, List<PropagationStatus>> updated = roleProvisioningManager.update(roleMod);
-
-        //moved after role provisioning manager
-        String roleOwner = null;
-        for (AttrMod attrMod : roleMod.getAttrsToUpdate()) {
-            if (attrMod.getSchema().isEmpty()) {
-                roleOwner = attrMod.getValuesToBeAdded().iterator().next();
-            }
-        }
-        if (roleOwner != null) {
-            roleOwnerMap.put(updated.getKey(), roleOwner);
-        }
-
-        final RoleTO after = roleTransfer.getRoleTO(roleDAO.authFetch(updated.getKey()));
-
-        result.setName(getName(after));
-
-        return after;
-    }
-
-    @Override
-    protected void deprovision(final Long id, final boolean unlink) {
-
-        taskExecutor.execute(
-                propagationManager.getRoleDeleteTaskIds(id, profile.getTask().getResource().getKey()));
-
-        if (unlink) {
-            final UserMod userMod = new UserMod();
-            userMod.setKey(id);
-            userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
-        }
-    }
-
-    @Override
-    protected void delete(final Long id) {
-        try {
-            taskExecutor.execute(
-                    propagationManager.getRoleDeleteTaskIds(id, profile.getTask().getResource().getKey()));
-        } catch (Exception e) {
-            // A propagation failure doesn't imply a synchronization failure.
-            // The propagation exception status will be reported into the propagation task execution.
-            LOG.error("Could not propagate user " + id, e);
-        }
-
-        roleProvisioningManager.delete(id);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/RoleSyncResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/RoleSyncResultHandlerImpl.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/RoleSyncResultHandlerImpl.java
new file mode 100644
index 0000000..2cada75
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/RoleSyncResultHandlerImpl.java
@@ -0,0 +1,169 @@
+/*
+ * 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.server.provisioning.java.sync;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
+import org.apache.syncope.common.lib.mod.AttrMod;
+import org.apache.syncope.common.lib.mod.RoleMod;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.to.PropagationStatus;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.server.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
+import org.apache.syncope.server.provisioning.api.sync.RoleSyncResultHandler;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+
+public class RoleSyncResultHandlerImpl extends AbstractSyncResultHandler implements RoleSyncResultHandler {
+
+    protected Map<Long, String> roleOwnerMap = new HashMap<>();
+
+    @Override
+    public Map<Long, String> getRoleOwnerMap() {
+        return this.roleOwnerMap;
+    }
+
+    @Override
+    protected AttributableUtil getAttributableUtil() {
+        return attrUtilFactory.getInstance(AttributableType.ROLE);
+    }
+
+    @Override
+    protected String getName(final AbstractSubjectTO subjectTO) {
+        return RoleTO.class.cast(subjectTO).getName();
+    }
+
+    @Override
+    protected AbstractSubjectTO getSubjectTO(final long key) {
+        try {
+            return roleTransfer.getRoleTO(key);
+        } catch (Exception e) {
+            LOG.warn("Error retrieving role {}", key, e);
+            return null;
+        }
+    }
+
+    @Override
+    protected AbstractSubjectMod getSubjectMod(
+            final AbstractSubjectTO subjectTO, final SyncDelta delta) {
+
+        return connObjectUtil.getAttributableMod(
+                subjectTO.getKey(),
+                delta.getObject(),
+                subjectTO,
+                profile.getTask(),
+                attrUtilFactory.getInstance(AttributableType.ROLE));
+    }
+
+    @Override
+    protected AbstractSubjectTO create(
+            final AbstractSubjectTO subjectTO, final SyncDelta _delta, final ProvisioningResult result) {
+
+        RoleTO roleTO = RoleTO.class.cast(subjectTO);
+
+        Map.Entry<Long, List<PropagationStatus>> created = roleProvisioningManager.create(roleTO, roleOwnerMap,
+                Collections.singleton(profile.getTask().getResource().getKey()));
+
+        roleTO = roleTransfer.getRoleTO(created.getKey());
+
+        result.setId(created.getKey());
+        result.setName(getName(subjectTO));
+
+        return roleTO;
+    }
+
+    @Override
+    protected AbstractSubjectTO link(
+            final AbstractSubjectTO before,
+            final ProvisioningResult result,
+            final boolean unlink) {
+
+        final RoleMod roleMod = new RoleMod();
+        roleMod.setKey(before.getKey());
+
+        if (unlink) {
+            roleMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+        } else {
+            roleMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
+        }
+
+        return roleTransfer.getRoleTO(rwfAdapter.update(roleMod).getResult());
+    }
+
+    @Override
+    protected AbstractSubjectTO update(
+            final AbstractSubjectTO before,
+            final AbstractSubjectMod subjectMod,
+            final SyncDelta delta,
+            final ProvisioningResult result) {
+
+        RoleMod roleMod = RoleMod.class.cast(subjectMod);
+
+        Map.Entry<Long, List<PropagationStatus>> updated = roleProvisioningManager.update(roleMod);
+
+        //moved after role provisioning manager
+        String roleOwner = null;
+        for (AttrMod attrMod : roleMod.getPlainAttrsToUpdate()) {
+            if (attrMod.getSchema().isEmpty()) {
+                roleOwner = attrMod.getValuesToBeAdded().iterator().next();
+            }
+        }
+        if (roleOwner != null) {
+            roleOwnerMap.put(updated.getKey(), roleOwner);
+        }
+
+        final RoleTO after = roleTransfer.getRoleTO(updated.getKey());
+
+        result.setName(getName(after));
+
+        return after;
+    }
+
+    @Override
+    protected void deprovision(final Long id, final boolean unlink) {
+
+        taskExecutor.execute(
+                propagationManager.getRoleDeleteTaskIds(id, profile.getTask().getResource().getKey()));
+
+        if (unlink) {
+            final UserMod userMod = new UserMod();
+            userMod.setKey(id);
+            userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+        }
+    }
+
+    @Override
+    protected void delete(final Long id) {
+        try {
+            taskExecutor.execute(
+                    propagationManager.getRoleDeleteTaskIds(id, profile.getTask().getResource().getKey()));
+        } catch (Exception e) {
+            // A propagation failure doesn't imply a synchronization failure.
+            // The propagation exception status will be reported into the propagation task execution.
+            LOG.error("Could not propagate user " + id, e);
+        }
+
+        roleProvisioningManager.delete(id);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncJobImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncJobImpl.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncJobImpl.java
index c47bff6..47a118f 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncJobImpl.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncJobImpl.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.server.provisioning.java.sync;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Map;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.mod.ReferenceMod;
@@ -34,11 +32,12 @@ import org.apache.syncope.server.persistence.api.entity.task.SyncTask;
 import org.apache.syncope.server.persistence.api.entity.user.UMapping;
 import org.apache.syncope.server.provisioning.api.Connector;
 import org.apache.syncope.server.provisioning.api.sync.ProvisioningProfile;
-import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
 import org.apache.syncope.server.provisioning.api.sync.SyncActions;
 import org.apache.syncope.server.misc.security.UnauthorizedRoleException;
 import org.apache.syncope.server.misc.spring.ApplicationContextProvider;
 import org.apache.syncope.server.provisioning.api.job.SyncJob;
+import org.apache.syncope.server.provisioning.api.sync.RoleSyncResultHandler;
+import org.apache.syncope.server.provisioning.api.sync.UserSyncResultHandler;
 import org.apache.syncope.server.workflow.api.RoleWorkflowAdapter;
 import org.identityconnectors.framework.common.objects.ObjectClass;
 import org.identityconnectors.framework.common.objects.SyncToken;
@@ -74,14 +73,14 @@ public class SyncJobImpl extends AbstractProvisioningJob<SyncTask, SyncActions>
                 roleMod.setRoleOwner(null);
                 roleMod.setUserOwner(null);
             } else {
-                Long userId = syncUtilities.findMatchingAttributableId(
+                Long userId = syncUtilities.findMatchingAttributableKey(
                         ObjectClass.ACCOUNT,
                         entry.getValue(),
                         rhandler.getProfile().getTask().getResource(),
                         rhandler.getProfile().getConnector());
 
                 if (userId == null) {
-                    Long roleId = syncUtilities.findMatchingAttributableId(
+                    Long roleId = syncUtilities.findMatchingAttributableKey(
                             ObjectClass.GROUP,
                             entry.getValue(),
                             rhandler.getProfile().getTask().getResource(),
@@ -109,27 +108,26 @@ public class SyncJobImpl extends AbstractProvisioningJob<SyncTask, SyncActions>
 
         LOG.debug("Execute synchronization with token {}", syncTask.getResource().getUsyncToken());
 
-        final List<ProvisioningResult> results = new ArrayList<>();
-
         final ProvisioningProfile<SyncTask, SyncActions> profile = new ProvisioningProfile<>(connector, syncTask);
-        profile.getActions().addAll(actions);
+        if (actions != null) {
+            profile.getActions().addAll(actions);
+        }
         profile.setDryRun(dryRun);
         profile.setResAct(getSyncPolicySpec(syncTask).getConflictResolutionAction());
-        profile.getResults().addAll(results);
 
         // Prepare handler for SyncDelta objects (users)
         final UserSyncResultHandler uhandler =
-                (UserSyncResultHandler) ApplicationContextProvider.getApplicationContext().getBeanFactory().createBean(
-                        UserSyncResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+                (UserSyncResultHandler) ApplicationContextProvider.getApplicationContext().getBeanFactory().
+                createBean(UserSyncResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
         uhandler.setProfile(profile);
 
         // Prepare handler for SyncDelta objects (roles/groups)
         final RoleSyncResultHandler rhandler =
-                (RoleSyncResultHandler) ApplicationContextProvider.getApplicationContext().getBeanFactory().createBean(
-                        RoleSyncResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+                (RoleSyncResultHandler) ApplicationContextProvider.getApplicationContext().getBeanFactory().
+                createBean(RoleSyncResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
         rhandler.setProfile(profile);
 
-        if (!profile.isDryRun()) {
+        if (actions != null && !profile.isDryRun()) {
             for (SyncActions action : actions) {
                 action.beforeAll(profile);
             }
@@ -189,13 +187,13 @@ public class SyncJobImpl extends AbstractProvisioningJob<SyncTask, SyncActions>
             LOG.error("While setting role owners", e);
         }
 
-        if (!profile.isDryRun()) {
+        if (actions != null && !profile.isDryRun()) {
             for (SyncActions action : actions) {
-                action.afterAll(profile, results);
+                action.afterAll(profile);
             }
         }
 
-        final String result = createReport(results, syncTask.getResource().getSyncTraceLevel(), dryRun);
+        final String result = createReport(profile.getResults(), syncTask.getResource().getSyncTraceLevel(), dryRun);
 
         LOG.debug("Sync result: {}", result);
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncUtilities.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncUtilities.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncUtilities.java
index 0d62487..299aba0 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncUtilities.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncUtilities.java
@@ -115,11 +115,12 @@ public class SyncUtilities {
     @Autowired
     protected AttributableUtilFactory attrUtilFactory;
 
-    public Long findMatchingAttributableId(
+    public Long findMatchingAttributableKey(
             final ObjectClass oclass,
             final String name,
             final ExternalResource resource,
             final Connector connector) {
+
         Long result = null;
 
         final AttributableUtil attrUtil = attrUtilFactory.getInstance(oclass);
@@ -137,15 +138,15 @@ public class SyncUtilities {
 
             ConnectorObject connObj = found.iterator().next();
             try {
-                final List<Long> subjectIds = findExisting(connObj.getUid().getUidValue(), connObj, resource, attrUtil);
-                if (subjectIds.isEmpty()) {
+                List<Long> subjectKeys = findExisting(connObj.getUid().getUidValue(), connObj, resource, attrUtil);
+                if (subjectKeys.isEmpty()) {
                     LOG.debug("No matching {} found for {}, aborting", attrUtil.getType(), connObj);
                 } else {
-                    if (subjectIds.size() > 1) {
-                        LOG.warn("More than one {} found {} - taking first only", attrUtil.getType(), subjectIds);
+                    if (subjectKeys.size() > 1) {
+                        LOG.warn("More than one {} found {} - taking first only", attrUtil.getType(), subjectKeys);
                     }
 
-                    result = subjectIds.iterator().next();
+                    result = subjectKeys.iterator().next();
                 }
             } catch (IllegalArgumentException e) {
                 LOG.warn(e.getMessage());
@@ -155,14 +156,14 @@ public class SyncUtilities {
         return result;
     }
 
-    public List<Long> findByAccountIdItem(
+    private List<Long> findByAccountIdItem(
             final String uid, final ExternalResource resource, final AttributableUtil attrUtil) {
         final List<Long> result = new ArrayList<>();
 
         final MappingItem accountIdItem = attrUtil.getAccountIdItem(resource);
         switch (accountIdItem.getIntMappingType()) {
-            case UserSchema:
-            case RoleSchema:
+            case UserPlainSchema:
+            case RolePlainSchema:
                 final PlainAttrValue value = attrUtil.newPlainAttrValue();
 
                 PlainSchema schema = plainSchemaDAO.find(accountIdItem.getIntAttrName(), attrUtil.plainSchemaClass());
@@ -227,7 +228,7 @@ public class SyncUtilities {
         return result;
     }
 
-    public List<Long> search(final SearchCond searchCond, final SubjectType type) {
+    private List<Long> search(final SearchCond searchCond, final SubjectType type) {
         final List<Long> result = new ArrayList<>();
 
         List<Subject<?, ?, ?>> subjects = searchDAO.search(
@@ -240,13 +241,13 @@ public class SyncUtilities {
         return result;
     }
 
-    public List<Long> findByCorrelationRule(
+    private List<Long> findByCorrelationRule(
             final ConnectorObject connObj, final SyncCorrelationRule rule, final SubjectType type) {
 
         return search(rule.getSearchCond(connObj), type);
     }
 
-    public List<Long> findByAttributableSearch(
+    private List<Long> findByAttributableSearch(
             final ConnectorObject connObj,
             final List<String> altSearchSchemas,
             final ExternalResource resource,
@@ -287,7 +288,7 @@ public class SyncUtilities {
             SearchCond nodeCond;
             // users: just id or username can be selected to be used
             // roles: just id or name can be selected to be used
-            if ("id".equalsIgnoreCase(schema)
+            if ("key".equalsIgnoreCase(schema)
                     || "username".equalsIgnoreCase(schema) || "name".equalsIgnoreCase(schema)) {
 
                 SubjectCond cond = new SubjectCond();

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserPushResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserPushResultHandler.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserPushResultHandler.java
deleted file mode 100644
index 3302388..0000000
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserPushResultHandler.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * 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.server.provisioning.java.sync;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import org.apache.syncope.common.lib.mod.UserMod;
-import org.apache.syncope.common.lib.to.AbstractSubjectTO;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.common.lib.types.PropagationByResource;
-import org.apache.syncope.common.lib.types.ResourceOperation;
-import org.apache.syncope.server.persistence.api.entity.Mapping;
-import org.apache.syncope.server.persistence.api.entity.MappingItem;
-import org.apache.syncope.server.persistence.api.entity.Subject;
-import org.apache.syncope.server.persistence.api.entity.user.User;
-import org.apache.syncope.server.provisioning.api.TimeoutException;
-import org.identityconnectors.framework.common.objects.ConnectorObject;
-import org.identityconnectors.framework.common.objects.ObjectClass;
-import org.identityconnectors.framework.common.objects.Uid;
-
-public class UserPushResultHandler extends AbstractSubjectPushResultHandler {
-
-    @Override
-    protected Subject<?, ?, ?> deprovision(final Subject<?, ?, ?> sbj) {
-        final UserTO before = userTransfer.getUserTO(userDAO.authFetch(sbj.getKey()));
-
-        final List<String> noPropResources = new ArrayList<>(before.getResources());
-        noPropResources.remove(profile.getTask().getResource().getKey());
-
-        taskExecutor.execute(propagationManager.getUserDeleteTaskIds(before.getKey(),
-                Collections.singleton(profile.getTask().getResource().getKey()), noPropResources));
-
-        return userDAO.authFetch(before.getKey());
-    }
-
-    @Override
-    protected Subject<?, ?, ?> provision(final Subject<?, ?, ?> sbj, final Boolean enabled) {
-        final UserTO before = userTransfer.getUserTO(userDAO.authFetch(sbj.getKey()));
-
-        final List<String> noPropResources = new ArrayList<>(before.getResources());
-        noPropResources.remove(profile.getTask().getResource().getKey());
-
-        final PropagationByResource propByRes = new PropagationByResource();
-        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
-
-        taskExecutor.execute(propagationManager.getUserCreateTaskIds(
-                before.getKey(),
-                enabled,
-                propByRes,
-                null,
-                Collections.unmodifiableCollection(before.getVirAttrs()),
-                Collections.unmodifiableCollection(before.getMemberships()),
-                noPropResources));
-
-        return userDAO.authFetch(before.getKey());
-    }
-
-    @Override
-    protected Subject<?, ?, ?> link(final Subject<?, ?, ?> sbj, final Boolean unlink) {
-        final UserMod userMod = new UserMod();
-        userMod.setKey(sbj.getKey());
-
-        if (unlink) {
-            userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
-        } else {
-            userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
-        }
-
-        uwfAdapter.update(userMod);
-
-        return userDAO.authFetch(userMod.getKey());
-    }
-
-    @Override
-    protected Subject<?, ?, ?> unassign(final Subject<?, ?, ?> sbj) {
-        final UserMod userMod = new UserMod();
-        userMod.setKey(sbj.getKey());
-        userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
-        uwfAdapter.update(userMod);
-        return deprovision(sbj);
-    }
-
-    @Override
-    protected Subject<?, ?, ?> assign(final Subject<?, ?, ?> sbj, final Boolean enabled) {
-        final UserMod userMod = new UserMod();
-        userMod.setKey(sbj.getKey());
-        userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
-        uwfAdapter.update(userMod);
-        return provision(sbj, enabled);
-    }
-
-    @Override
-    protected String getName(final Subject<?, ?, ?> subject) {
-        return User.class.cast(subject).getUsername();
-    }
-
-    @Override
-    protected AbstractSubjectTO getSubjectTO(final long key) {
-        try {
-            return userTransfer.getUserTO(userDAO.authFetch(key));
-        } catch (Exception e) {
-            LOG.warn("Error retrieving user {}", key, e);
-            return null;
-        }
-    }
-
-    @Override
-    protected Subject<?, ?, ?> getSubject(final long key) {
-        try {
-            return userDAO.authFetch(key);
-        } catch (Exception e) {
-            LOG.warn("Error retrieving user {}", key, e);
-            return null;
-        }
-    }
-
-    @Override
-    protected ConnectorObject getRemoteObject(final String accountId) {
-        ConnectorObject obj = null;
-
-        try {
-            final Uid uid = new Uid(accountId);
-
-            obj = profile.getConnector().getObject(
-                    ObjectClass.ACCOUNT,
-                    uid,
-                    profile.getConnector().getOperationOptions(Collections.<MappingItem>emptySet()));
-
-        } catch (TimeoutException toe) {
-            LOG.debug("Request timeout", toe);
-            throw toe;
-        } catch (RuntimeException ignore) {
-            LOG.debug("While resolving {}", accountId, ignore);
-        }
-        return obj;
-    }
-
-    @Override
-    protected Mapping<?> getMapping() {
-        return profile.getTask().getResource().getUmapping();
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserPushResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserPushResultHandlerImpl.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserPushResultHandlerImpl.java
new file mode 100644
index 0000000..57fe4a7
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserPushResultHandlerImpl.java
@@ -0,0 +1,160 @@
+/*
+ * 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.server.provisioning.java.sync;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.PropagationByResource;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.server.persistence.api.entity.Mapping;
+import org.apache.syncope.server.persistence.api.entity.MappingItem;
+import org.apache.syncope.server.persistence.api.entity.Subject;
+import org.apache.syncope.server.persistence.api.entity.user.User;
+import org.apache.syncope.server.provisioning.api.TimeoutException;
+import org.apache.syncope.server.provisioning.api.sync.UserPushResultHandler;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.Uid;
+
+public class UserPushResultHandlerImpl extends AbstractPushResultHandler implements UserPushResultHandler {
+
+    @Override
+    protected Subject<?, ?, ?> deprovision(final Subject<?, ?, ?> sbj) {
+        final UserTO before = userTransfer.getUserTO(sbj.getKey());
+
+        final List<String> noPropResources = new ArrayList<>(before.getResources());
+        noPropResources.remove(profile.getTask().getResource().getKey());
+
+        taskExecutor.execute(propagationManager.getUserDeleteTaskIds(before.getKey(),
+                Collections.singleton(profile.getTask().getResource().getKey()), noPropResources));
+
+        return userDAO.authFetch(before.getKey());
+    }
+
+    @Override
+    protected Subject<?, ?, ?> provision(final Subject<?, ?, ?> sbj, final Boolean enabled) {
+        final UserTO before = userTransfer.getUserTO(sbj.getKey());
+
+        final List<String> noPropResources = new ArrayList<>(before.getResources());
+        noPropResources.remove(profile.getTask().getResource().getKey());
+
+        final PropagationByResource propByRes = new PropagationByResource();
+        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
+
+        taskExecutor.execute(propagationManager.getUserCreateTaskIds(
+                before.getKey(),
+                enabled,
+                propByRes,
+                null,
+                Collections.unmodifiableCollection(before.getVirAttrs()),
+                Collections.unmodifiableCollection(before.getMemberships()),
+                noPropResources));
+
+        return userDAO.authFetch(before.getKey());
+    }
+
+    @Override
+    protected Subject<?, ?, ?> link(final Subject<?, ?, ?> sbj, final Boolean unlink) {
+        final UserMod userMod = new UserMod();
+        userMod.setKey(sbj.getKey());
+
+        if (unlink) {
+            userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+        } else {
+            userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
+        }
+
+        uwfAdapter.update(userMod);
+
+        return userDAO.authFetch(userMod.getKey());
+    }
+
+    @Override
+    protected Subject<?, ?, ?> unassign(final Subject<?, ?, ?> sbj) {
+        final UserMod userMod = new UserMod();
+        userMod.setKey(sbj.getKey());
+        userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+        uwfAdapter.update(userMod);
+        return deprovision(sbj);
+    }
+
+    @Override
+    protected Subject<?, ?, ?> assign(final Subject<?, ?, ?> sbj, final Boolean enabled) {
+        final UserMod userMod = new UserMod();
+        userMod.setKey(sbj.getKey());
+        userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
+        uwfAdapter.update(userMod);
+        return provision(sbj, enabled);
+    }
+
+    @Override
+    protected String getName(final Subject<?, ?, ?> subject) {
+        return User.class.cast(subject).getUsername();
+    }
+
+    @Override
+    protected AbstractSubjectTO getSubjectTO(final long key) {
+        try {
+            return userTransfer.getUserTO(key);
+        } catch (Exception e) {
+            LOG.warn("Error retrieving user {}", key, e);
+            return null;
+        }
+    }
+
+    @Override
+    protected Subject<?, ?, ?> getSubject(final long key) {
+        try {
+            return userDAO.authFetch(key);
+        } catch (Exception e) {
+            LOG.warn("Error retrieving user {}", key, e);
+            return null;
+        }
+    }
+
+    @Override
+    protected ConnectorObject getRemoteObject(final String accountId) {
+        ConnectorObject obj = null;
+
+        try {
+            final Uid uid = new Uid(accountId);
+
+            obj = profile.getConnector().getObject(
+                    ObjectClass.ACCOUNT,
+                    uid,
+                    profile.getConnector().getOperationOptions(Collections.<MappingItem>emptySet()));
+
+        } catch (TimeoutException toe) {
+            LOG.debug("Request timeout", toe);
+            throw toe;
+        } catch (RuntimeException ignore) {
+            LOG.debug("While resolving {}", accountId, ignore);
+        }
+        return obj;
+    }
+
+    @Override
+    protected Mapping<?> getMapping() {
+        return profile.getTask().getResource().getUmapping();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserSyncResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserSyncResultHandler.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserSyncResultHandler.java
deleted file mode 100644
index 58e81ed..0000000
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserSyncResultHandler.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * 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.server.provisioning.java.sync;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
-import org.apache.syncope.common.lib.mod.UserMod;
-import org.apache.syncope.common.lib.to.AbstractSubjectTO;
-import org.apache.syncope.common.lib.to.PropagationStatus;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.common.lib.types.AttributableType;
-import org.apache.syncope.server.persistence.api.entity.AttributableUtil;
-import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
-import org.identityconnectors.framework.common.objects.SyncDelta;
-import org.quartz.JobExecutionException;
-
-public class UserSyncResultHandler extends AbstractSubjectSyncResultHandler {
-
-    @Override
-    protected AttributableUtil getAttributableUtil() {
-        return attrUtilFactory.getInstance(AttributableType.USER);
-    }
-
-    @Override
-    protected String getName(final AbstractSubjectTO subjectTO) {
-        return UserTO.class.cast(subjectTO).getUsername();
-    }
-
-    @Override
-    protected AbstractSubjectTO getSubjectTO(final long key) {
-        try {
-            return userTransfer.getUserTO(userDAO.authFetch(key));
-        } catch (Exception e) {
-            LOG.warn("Error retrieving user {}", key, e);
-            return null;
-        }
-    }
-
-    @Override
-    protected AbstractSubjectMod getSubjectMod(
-            final AbstractSubjectTO subjectTO, final SyncDelta delta) {
-
-        return connObjectUtil.getAttributableMod(
-                subjectTO.getKey(),
-                delta.getObject(),
-                subjectTO,
-                profile.getTask(),
-                getAttributableUtil());
-    }
-
-    @Override
-    protected AbstractSubjectTO create(
-            final AbstractSubjectTO subjectTO, final SyncDelta delta, final ProvisioningResult result) {
-
-        UserTO userTO = UserTO.class.cast(subjectTO);
-
-        Boolean enabled = syncUtilities.readEnabled(delta.getObject(), profile.getTask());
-        //Delegate User Workflow Creation and its Propagation to provisioning manager
-        Map.Entry<Long, List<PropagationStatus>> created = userProvisioningManager.create(userTO, true, true, enabled,
-                Collections.singleton(profile.getTask().getResource().getKey()));
-
-        userTO = userTransfer.getUserTO(userDAO.authFetch(created.getKey()));
-
-        result.setId(created.getKey());
-
-        return userTO;
-    }
-
-    @Override
-    protected List<ProvisioningResult> link(SyncDelta delta, List<Long> subjects, AttributableUtil attrUtil,
-            boolean unlink) throws JobExecutionException {
-        return super.link(delta, subjects, attrUtil, unlink); //To change body of generated methods, choose Tools | Templates.
-    }
-
-    @Override
-    protected AbstractSubjectTO link(
-            final AbstractSubjectTO before,
-            final ProvisioningResult result,
-            final boolean unlink) {
-
-        final UserMod userMod = new UserMod();
-        userMod.setKey(before.getKey());
-
-        if (unlink) {
-            userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
-        } else {
-            userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
-        }
-
-        return userTransfer.getUserTO(userDAO.authFetch(uwfAdapter.update(userMod).getResult().getKey().getKey()));
-    }
-
-    @Override
-    protected AbstractSubjectTO update(
-            final AbstractSubjectTO before,
-            final AbstractSubjectMod subjectMod,
-            final SyncDelta delta,
-            final ProvisioningResult result) {
-
-        final UserMod userMod = UserMod.class.cast(subjectMod);
-        final Boolean enabled = syncUtilities.readEnabled(delta.getObject(), profile.getTask());
-
-        Map.Entry<Long, List<PropagationStatus>> updated = userProvisioningManager.update(userMod, before.getKey(),
-                result, enabled, Collections.singleton(profile.getTask().getResource().getKey()));
-
-        return userTransfer.getUserTO(userDAO.authFetch(updated.getKey()));
-    }
-
-    @Override
-    protected void deprovision(
-            final Long key,
-            final boolean unlink) {
-
-        taskExecutor.execute(
-                propagationManager.getUserDeleteTaskIds(key, profile.getTask().getResource().getKey()));
-
-        if (unlink) {
-            final UserMod userMod = new UserMod();
-            userMod.setKey(key);
-            userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
-        }
-    }
-
-    @Override
-    protected void delete(final Long key) {
-        try {
-            userProvisioningManager.
-                    delete(key, Collections.<String>singleton(profile.getTask().getResource().getKey()));
-        } catch (Exception e) {
-            // A propagation failure doesn't imply a synchronization failure.
-            // The propagation exception status will be reported into the propagation task execution.
-            LOG.error("Could not propagate user " + key, e);
-        }
-
-        uwfAdapter.delete(key);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserSyncResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserSyncResultHandlerImpl.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserSyncResultHandlerImpl.java
new file mode 100644
index 0000000..9a7e1ba
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserSyncResultHandlerImpl.java
@@ -0,0 +1,149 @@
+/*
+ * 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.server.provisioning.java.sync;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.to.PropagationStatus;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.server.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
+import org.apache.syncope.server.provisioning.api.sync.UserSyncResultHandler;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+
+public class UserSyncResultHandlerImpl extends AbstractSyncResultHandler implements UserSyncResultHandler {
+
+    @Override
+    protected AttributableUtil getAttributableUtil() {
+        return attrUtilFactory.getInstance(AttributableType.USER);
+    }
+
+    @Override
+    protected String getName(final AbstractSubjectTO subjectTO) {
+        return UserTO.class.cast(subjectTO).getUsername();
+    }
+
+    @Override
+    protected AbstractSubjectTO getSubjectTO(final long key) {
+        try {
+            return userTransfer.getUserTO(key);
+        } catch (Exception e) {
+            LOG.warn("Error retrieving user {}", key, e);
+            return null;
+        }
+    }
+
+    @Override
+    protected AbstractSubjectMod getSubjectMod(
+            final AbstractSubjectTO subjectTO, final SyncDelta delta) {
+
+        return connObjectUtil.getAttributableMod(
+                subjectTO.getKey(),
+                delta.getObject(),
+                subjectTO,
+                profile.getTask(),
+                getAttributableUtil());
+    }
+
+    @Override
+    protected AbstractSubjectTO create(
+            final AbstractSubjectTO subjectTO, final SyncDelta delta, final ProvisioningResult result) {
+
+        UserTO userTO = UserTO.class.cast(subjectTO);
+
+        Boolean enabled = syncUtilities.readEnabled(delta.getObject(), profile.getTask());
+        //Delegate User Workflow Creation and its Propagation to provisioning manager
+        Map.Entry<Long, List<PropagationStatus>> created = userProvisioningManager.create(userTO, true, true, enabled,
+                Collections.singleton(profile.getTask().getResource().getKey()));
+
+        userTO = userTransfer.getUserTO(created.getKey());
+
+        result.setId(created.getKey());
+
+        return userTO;
+    }
+
+    @Override
+    protected AbstractSubjectTO link(
+            final AbstractSubjectTO before,
+            final ProvisioningResult result,
+            final boolean unlink) {
+
+        final UserMod userMod = new UserMod();
+        userMod.setKey(before.getKey());
+
+        if (unlink) {
+            userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+        } else {
+            userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
+        }
+
+        return userTransfer.getUserTO(uwfAdapter.update(userMod).getResult().getKey().getKey());
+    }
+
+    @Override
+    protected AbstractSubjectTO update(
+            final AbstractSubjectTO before,
+            final AbstractSubjectMod subjectMod,
+            final SyncDelta delta,
+            final ProvisioningResult result) {
+
+        final UserMod userMod = UserMod.class.cast(subjectMod);
+        final Boolean enabled = syncUtilities.readEnabled(delta.getObject(), profile.getTask());
+
+        Map.Entry<Long, List<PropagationStatus>> updated = userProvisioningManager.update(userMod, before.getKey(),
+                result, enabled, Collections.singleton(profile.getTask().getResource().getKey()));
+
+        return userTransfer.getUserTO(updated.getKey());
+    }
+
+    @Override
+    protected void deprovision(
+            final Long key,
+            final boolean unlink) {
+
+        taskExecutor.execute(
+                propagationManager.getUserDeleteTaskIds(key, profile.getTask().getResource().getKey()));
+
+        if (unlink) {
+            final UserMod userMod = new UserMod();
+            userMod.setKey(key);
+            userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+        }
+    }
+
+    @Override
+    protected void delete(final Long key) {
+        try {
+            userProvisioningManager.
+                    delete(key, Collections.<String>singleton(profile.getTask().getResource().getKey()));
+        } catch (Exception e) {
+            // A propagation failure doesn't imply a synchronization failure.
+            // The propagation exception status will be reported into the propagation task execution.
+            LOG.error("Could not propagate user " + key, e);
+        }
+
+        uwfAdapter.delete(key);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.html.vm
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.html.vm b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.html.vm
index 007b09b..8240c7b 100644
--- a/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.html.vm
+++ b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.html.vm
@@ -18,19 +18,19 @@ under the License.
 -->
 <html>
 <body>
-<h3>Hi $user.getAttrMap().get("firstname").getValues().get(0) $user.getAttrMap().get("surname").getValues().get(0), welcome to Syncope!</h3>
+<h3>Hi $user.getPlainAttrMap().get("firstname").getValues().get(0) $user.getPlainAttrMap().get("surname").getValues().get(0), welcome to Syncope!</h3>
 
 <p>
    Your username is $user.getUsername().<br/>
-   Your email address is $user.getAttrMap().get("email").getValues().get(0).
-   Your email address inside a <a href="http://localhost/?email=$esc.url($user.getAttrMap().get("email").getValues().get(0))">link</a>.
+   Your email address is $user.getPlainAttrMap().get("email").getValues().get(0).
+   Your email address inside a <a href="http://localhost/?email=$esc.url($user.getPlainAttrMap().get("email").getValues().get(0))">link</a>.
 </p>
 
 <p>
     This message was sent to the following recipients:
 <ul>
 #foreach($recipient in $recipients)
-  <li>$recipient.getAttrMap().get("email").getValues().get(0)</li>
+  <li>$recipient.getPlainAttrMap().get("email").getValues().get(0)</li>
 #end
 </ul>
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm
index b79b317..fc8e398 100644
--- a/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm
+++ b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm
@@ -14,15 +14,15 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-Hi $user.getAttrMap().get("firstname").getValues().get(0) $user.getAttrMap().get("surname").getValues().get(0), welcome to Syncope!
+Hi $user.getPlainAttrMap().get("firstname").getValues().get(0) $user.getPlainAttrMap().get("surname").getValues().get(0), welcome to Syncope!
 
 Your username is $user.getUsername().
-Your email address is $user.getAttrMap().get("email").getValues().get(0).
-Your email address inside a link: http://localhost/?email=$esc.url($user.getAttrMap().get("email").getValues().get(0)) .
+Your email address is $user.getPlainAttrMap().get("email").getValues().get(0).
+Your email address inside a link: http://localhost/?email=$esc.url($user.getPlainAttrMap().get("email").getValues().get(0)) .
 
 This message was sent to the following recipients:
 #foreach($recipient in $recipients)
-   * $recipient.getAttrMap().get("surname").getValues().get(0)
+   * $recipient.getPlainAttrMap().get("surname").getValues().get(0)
 #end
 
 because one of the following events occurred:

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/resources/provisioningContext.xml
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/resources/provisioningContext.xml b/syncope620/server/provisioning-java/src/main/resources/provisioningContext.xml
index d3fa665..3b4301e 100644
--- a/syncope620/server/provisioning-java/src/main/resources/provisioningContext.xml
+++ b/syncope620/server/provisioning-java/src/main/resources/provisioningContext.xml
@@ -20,10 +20,16 @@ under the License.
 <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:task="http://www.springframework.org/schema/task"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/context
-                           http://www.springframework.org/schema/context/spring-context.xsd">
+                           http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/task
+                           http://www.springframework.org/schema/task/spring-task.xsd">
+  
+  <task:annotation-driven executor="connectorExecutor"/>
+  <task:executor id="connectorExecutor" pool-size="10"/>
   
   <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"
         lazy-init="false" depends-on="nonJPAdbInitializer">
@@ -34,7 +40,7 @@ under the License.
     <property name="dataSource" ref="dataSource"/>
     <property name="transactionManager" ref="transactionManager"/>
     <property name="jobFactory">
-      <bean class="org.springframework.scheduling.quartz.SpringBeanJobFactory"/>
+      <bean class="org.apache.syncope.server.provisioning.java.job.SpringBeanJobFactory"/>
     </property>
     <property name="quartzProperties">
       <props>

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/test/java/org/apache/syncope/server/provisioning/java/data/ResourceDataBinderTest.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/test/java/org/apache/syncope/server/provisioning/java/data/ResourceDataBinderTest.java b/syncope620/server/provisioning-java/src/test/java/org/apache/syncope/server/provisioning/java/data/ResourceDataBinderTest.java
index 8b8a39b..34d73fe 100644
--- a/syncope620/server/provisioning-java/src/test/java/org/apache/syncope/server/provisioning/java/data/ResourceDataBinderTest.java
+++ b/syncope620/server/provisioning-java/src/test/java/org/apache/syncope/server/provisioning/java/data/ResourceDataBinderTest.java
@@ -115,7 +115,7 @@ public class ResourceDataBinderTest extends AbstractTest {
 
         MappingItemTO item = new MappingItemTO();
         item.setIntAttrName("userId");
-        item.setIntMappingType(IntMappingType.UserSchema);
+        item.setIntMappingType(IntMappingType.UserPlainSchema);
         item.setExtAttrName("campo1");
         item.setAccountid(true);
         item.setMandatoryCondition("false");

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/AbstractServiceImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/AbstractServiceImpl.java b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/AbstractServiceImpl.java
index 9168ab1..94003ab 100644
--- a/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/AbstractServiceImpl.java
+++ b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/AbstractServiceImpl.java
@@ -163,7 +163,7 @@ abstract class AbstractServiceImpl implements JAXRSService {
             return Collections.<OrderByClause>emptyList();
         }
 
-        List<OrderByClause> result = new ArrayList<OrderByClause>();
+        List<OrderByClause> result = new ArrayList<>();
 
         for (String clause : orderBy.split(",")) {
             String[] elems = clause.split(" ");
@@ -195,7 +195,7 @@ abstract class AbstractServiceImpl implements JAXRSService {
     protected <T extends AbstractBaseBean> PagedResult<T> buildPagedResult(
             final List<T> list, final int page, final int size, final int totalCount) {
 
-        PagedResult<T> result = new PagedResult<T>();
+        PagedResult<T> result = new PagedResult<>();
         result.getResult().addAll(list);
 
         result.setPage(page);

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/LoggerServiceImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/LoggerServiceImpl.java b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/LoggerServiceImpl.java
index 09995b1..7651d05 100644
--- a/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/LoggerServiceImpl.java
+++ b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/LoggerServiceImpl.java
@@ -80,7 +80,7 @@ public class LoggerServiceImpl extends AbstractServiceImpl implements LoggerServ
     public LoggerTO read(final LoggerType type, final String name) {
         List<LoggerTO> logger = list(type);
         for (LoggerTO l : logger) {
-            if (l.getName().equals(name)) {
+            if (l.getKey().equals(name)) {
                 return l;
             }
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/ReportServiceImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/ReportServiceImpl.java b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/ReportServiceImpl.java
index 3b30337..32306e9 100644
--- a/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/ReportServiceImpl.java
+++ b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/ReportServiceImpl.java
@@ -92,14 +92,14 @@ public class ReportServiceImpl extends AbstractServiceImpl implements ReportServ
     }
 
     @Override
-    public ReportExecTO readExecution(final Long executionId) {
-        return logic.readExecution(executionId);
+    public ReportExecTO readExecution(final Long executionKey) {
+        return logic.readExecution(executionKey);
     }
 
     @Override
-    public Response exportExecutionResult(final Long executionId, final ReportExecExportFormat fmt) {
+    public Response exportExecutionResult(final Long executionKey, final ReportExecExportFormat fmt) {
         final ReportExecExportFormat format = (fmt == null) ? ReportExecExportFormat.XML : fmt;
-        final ReportExec reportExec = logic.getAndCheckReportExec(executionId);
+        final ReportExec reportExec = logic.getAndCheckReportExec(executionKey);
         StreamingOutput sout = new StreamingOutput() {
 
             @Override
@@ -125,7 +125,7 @@ public class ReportServiceImpl extends AbstractServiceImpl implements ReportServ
     }
 
     @Override
-    public void deleteExecution(final Long executionId) {
-        logic.deleteExecution(executionId);
+    public void deleteExecution(final Long executionKey) {
+        logic.deleteExecution(executionKey);
     }
 }


[15/15] syncope git commit: Merge branch '2_0_X' of https://git-wip-us.apache.org/repos/asf/syncope into 2_0_X

Posted by il...@apache.org.
Merge branch '2_0_X' of https://git-wip-us.apache.org/repos/asf/syncope into 2_0_X


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/4e1ae7ac
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/4e1ae7ac
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/4e1ae7ac

Branch: refs/heads/2_0_X
Commit: 4e1ae7ac91428ea1a4837f2f682e23c5c9d42a1c
Parents: 80589a1 8e45a8e
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Fri Jan 23 17:40:56 2015 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Fri Jan 23 17:40:56 2015 +0100

----------------------------------------------------------------------
 cli/pom.xml                                     | 186 +++++++++++++++++++
 .../java/org/apache/syncope/cli/SyncopeAdm.java |  78 ++++++++
 .../org/apache/syncope/cli/SyncopeServices.java |  41 ++++
 .../syncope/cli/commands/LoggerCommand.java     | 117 ++++++++++++
 .../cli/validators/DebugLevelValidator.java     |  61 ++++++
 cli/src/main/resources/log4j2.xml               |  58 ++++++
 cli/src/main/resources/syncope.properties       |  19 ++
 pom.xml                                         |   9 +-
 8 files changed, 568 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/4e1ae7ac/pom.xml
----------------------------------------------------------------------


[04/15] syncope git commit: FIT server integration tests

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/RestServiceExceptionMapper.java
----------------------------------------------------------------------
diff --git a/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/RestServiceExceptionMapper.java b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/RestServiceExceptionMapper.java
index 19a4a0d..fc78a54 100644
--- a/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/RestServiceExceptionMapper.java
+++ b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/RestServiceExceptionMapper.java
@@ -45,6 +45,7 @@ import org.apache.syncope.common.rest.api.RESTHeaders;
 import org.apache.syncope.server.misc.security.UnauthorizedRoleException;
 import org.apache.syncope.server.persistence.api.attrvalue.validation.InvalidEntityException;
 import org.apache.syncope.server.persistence.api.attrvalue.validation.ParsingValidationException;
+import org.apache.syncope.server.persistence.api.dao.DuplicateException;
 import org.apache.syncope.server.persistence.api.dao.NotFoundException;
 import org.apache.syncope.server.workflow.api.WorkflowException;
 import org.identityconnectors.framework.common.exceptions.ConfigurationException;
@@ -89,7 +90,7 @@ public class RestServiceExceptionMapper implements ExceptionMapper<Exception>, R
                     header(HttpHeaders.WWW_AUTHENTICATE, BASIC_REALM_UNAUTHORIZED);
         } else if (ex instanceof UnauthorizedRoleException) {
             builder = builder(Response.Status.UNAUTHORIZED, ClientExceptionType.UnauthorizedRole, getExMessage(ex));
-        } else if (ex instanceof EntityExistsException) {
+        } else if (ex instanceof EntityExistsException || ex instanceof DuplicateException) {
             builder = builder(Response.Status.CONFLICT, ClientExceptionType.EntityExists, getExMessage(ex));
         } else if (ex instanceof DataIntegrityViolationException) {
             builder = builder(Response.Status.CONFLICT, ClientExceptionType.DataIntegrityViolation, getExMessage(ex));


[08/15] syncope git commit: FIT server integration tests

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/LoggerLogic.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/LoggerLogic.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/LoggerLogic.java
index 47db825..3c9a614 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/LoggerLogic.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/LoggerLogic.java
@@ -80,9 +80,9 @@ public class LoggerLogic extends AbstractTransactionalLogic<LoggerTO> {
 
     private List<LoggerTO> list(final LoggerType type) {
         List<LoggerTO> result = new ArrayList<>();
-        for (Logger syncopeLogger : loggerDAO.findAll(type)) {
+        for (Logger logger : loggerDAO.findAll(type)) {
             LoggerTO loggerTO = new LoggerTO();
-            BeanUtils.copyProperties(syncopeLogger, loggerTO);
+            BeanUtils.copyProperties(logger, loggerTO);
             result.add(loggerTO);
         }
 
@@ -102,9 +102,9 @@ public class LoggerLogic extends AbstractTransactionalLogic<LoggerTO> {
 
         for (LoggerTO logger : list(LoggerType.AUDIT)) {
             try {
-                result.add(AuditLoggerName.fromLoggerName(logger.getName()));
+                result.add(AuditLoggerName.fromLoggerName(logger.getKey()));
             } catch (Exception e) {
-                LOG.warn("Unexpected audit logger name: {}", logger.getName(), e);
+                LOG.warn("Unexpected audit logger name: {}", logger.getKey(), e);
             }
         }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/ReportLogic.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/ReportLogic.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/ReportLogic.java
index bb369ab..e8502ec 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/ReportLogic.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/ReportLogic.java
@@ -55,7 +55,7 @@ import org.apache.syncope.server.persistence.api.entity.ReportExec;
 import org.apache.syncope.server.provisioning.api.data.ReportDataBinder;
 import org.apache.syncope.server.provisioning.api.job.JobNamer;
 import org.apache.syncope.server.logic.init.ImplementationClassNamesLoader;
-import org.apache.syncope.server.logic.init.JobInstanceLoader;
+import org.apache.syncope.server.provisioning.api.job.JobInstanceLoader;
 import org.apache.syncope.server.logic.report.Reportlet;
 import org.apache.syncope.server.logic.report.ReportletConfClass;
 import org.apache.syncope.server.logic.report.TextSerializer;
@@ -206,20 +206,20 @@ public class ReportLogic extends AbstractTransactionalLogic<ReportTO> {
     }
 
     @PreAuthorize("hasRole('REPORT_READ')")
-    public ReportTO read(final Long reportId) {
-        Report report = reportDAO.find(reportId);
+    public ReportTO read(final Long reportKey) {
+        Report report = reportDAO.find(reportKey);
         if (report == null) {
-            throw new NotFoundException("Report " + reportId);
+            throw new NotFoundException("Report " + reportKey);
         }
         return binder.getReportTO(report);
     }
 
     @PreAuthorize("hasRole('REPORT_READ')")
     @Transactional(readOnly = true)
-    public ReportExecTO readExecution(final Long executionId) {
-        ReportExec reportExec = reportExecDAO.find(executionId);
+    public ReportExecTO readExecution(final Long executionKey) {
+        ReportExec reportExec = reportExecDAO.find(executionKey);
         if (reportExec == null) {
-            throw new NotFoundException("Report execution " + executionId);
+            throw new NotFoundException("Report execution " + executionKey);
         }
         return binder.getReportExecTO(reportExec);
     }
@@ -291,10 +291,10 @@ public class ReportLogic extends AbstractTransactionalLogic<ReportTO> {
     }
 
     @PreAuthorize("hasRole('REPORT_READ')")
-    public ReportExec getAndCheckReportExec(final Long executionId) {
-        ReportExec reportExec = reportExecDAO.find(executionId);
+    public ReportExec getAndCheckReportExec(final Long executionKey) {
+        ReportExec reportExec = reportExecDAO.find(executionKey);
         if (reportExec == null) {
-            throw new NotFoundException("Report execution " + executionId);
+            throw new NotFoundException("Report execution " + executionKey);
         }
         if (!ReportExecStatus.SUCCESS.name().equals(reportExec.getStatus()) || reportExec.getExecResult() == null) {
             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidReportExec);
@@ -307,10 +307,10 @@ public class ReportLogic extends AbstractTransactionalLogic<ReportTO> {
     }
 
     @PreAuthorize("hasRole('REPORT_EXECUTE')")
-    public ReportExecTO execute(final Long reportId) {
-        Report report = reportDAO.find(reportId);
+    public ReportExecTO execute(final Long reportKey) {
+        Report report = reportDAO.find(reportKey);
         if (report == null) {
-            throw new NotFoundException("Report " + reportId);
+            throw new NotFoundException("Report " + reportKey);
         }
 
         try {
@@ -327,7 +327,7 @@ public class ReportLogic extends AbstractTransactionalLogic<ReportTO> {
         }
 
         ReportExecTO result = new ReportExecTO();
-        result.setReport(reportId);
+        result.setReport(reportKey);
         result.setStartDate(new Date());
         result.setStatus(ReportExecStatus.STARTED.name());
         result.setMessage("Job fired; waiting for results...");
@@ -336,10 +336,10 @@ public class ReportLogic extends AbstractTransactionalLogic<ReportTO> {
     }
 
     @PreAuthorize("hasRole('REPORT_DELETE')")
-    public ReportTO delete(final Long reportId) {
-        Report report = reportDAO.find(reportId);
+    public ReportTO delete(final Long reportKey) {
+        Report report = reportDAO.find(reportKey);
         if (report == null) {
-            throw new NotFoundException("Report " + reportId);
+            throw new NotFoundException("Report " + reportKey);
         }
 
         ReportTO deletedReport = binder.getReportTO(report);
@@ -349,10 +349,10 @@ public class ReportLogic extends AbstractTransactionalLogic<ReportTO> {
     }
 
     @PreAuthorize("hasRole('REPORT_DELETE')")
-    public ReportExecTO deleteExecution(final Long executionId) {
-        ReportExec reportExec = reportExecDAO.find(executionId);
+    public ReportExecTO deleteExecution(final Long executionKey) {
+        ReportExec reportExec = reportExecDAO.find(executionKey);
         if (reportExec == null) {
-            throw new NotFoundException("Report execution " + executionId);
+            throw new NotFoundException("Report execution " + executionKey);
         }
 
         ReportExecTO reportExecToDelete = binder.getReportExecTO(reportExec);

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/SpringBeanJobFactory.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/SpringBeanJobFactory.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/SpringBeanJobFactory.java
deleted file mode 100644
index 9eccaea..0000000
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/SpringBeanJobFactory.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.server.logic;
-
-import org.apache.syncope.server.provisioning.api.job.JobNamer;
-import org.apache.syncope.server.logic.init.JobInstanceLoader;
-import org.quartz.SchedulerContext;
-import org.quartz.spi.TriggerFiredBundle;
-import org.springframework.beans.BeanWrapper;
-import org.springframework.beans.MutablePropertyValues;
-import org.springframework.beans.PropertyAccessorFactory;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ConfigurableApplicationContext;
-
-public class SpringBeanJobFactory extends org.springframework.scheduling.quartz.SpringBeanJobFactory {
-
-    private String[] ignoredUnknownProperties;
-
-    private SchedulerContext schedulerContext;
-
-    @Override
-    public void setIgnoredUnknownProperties(final String[] ignoredUnknownProperties) {
-        String[] defensiveCopy = ignoredUnknownProperties.clone();
-        super.setIgnoredUnknownProperties(defensiveCopy);
-        this.ignoredUnknownProperties = defensiveCopy;
-    }
-
-    @Override
-    public void setSchedulerContext(final SchedulerContext schedulerContext) {
-        super.setSchedulerContext(schedulerContext);
-        this.schedulerContext = schedulerContext;
-    }
-
-    /**
-     * An implementation of SpringBeanJobFactory that retrieves the bean from the Spring context so that autowiring and
-     * transactions work.
-     *
-     * {@inheritDoc}
-     */
-    @Override
-    protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
-        final ApplicationContext ctx = ((ConfigurableApplicationContext) schedulerContext.get("applicationContext"));
-
-        // Try to re-create job bean from underlying task (useful for managing failover scenarios)
-        if (!ctx.containsBean(bundle.getJobDetail().getKey().getName())) {
-            Long taskId = JobNamer.getTaskIdFromJobName(bundle.getJobDetail().getKey().getName());
-            if (taskId != null) {
-                JobInstanceLoader jobInstanceLoader = ctx.getBean(JobInstanceLoader.class);
-                jobInstanceLoader.registerTaskJob(taskId);
-            }
-
-            Long reportId = JobNamer.getReportIdFromJobName(bundle.getJobDetail().getKey().getName());
-            if (reportId != null) {
-                JobInstanceLoader jobInstanceLoader = ctx.getBean(JobInstanceLoader.class);
-                jobInstanceLoader.registerReportJob(reportId);
-            }
-        }
-
-        final Object job = ctx.getBean(bundle.getJobDetail().getKey().getName());
-        final BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(job);
-        if (isEligibleForPropertyPopulation(wrapper.getWrappedInstance())) {
-            final MutablePropertyValues pvs = new MutablePropertyValues();
-            if (this.schedulerContext != null) {
-                pvs.addPropertyValues(this.schedulerContext);
-            }
-            pvs.addPropertyValues(bundle.getJobDetail().getJobDataMap());
-            pvs.addPropertyValues(bundle.getTrigger().getJobDataMap());
-            if (this.ignoredUnknownProperties == null) {
-                wrapper.setPropertyValues(pvs, true);
-            } else {
-                for (String propName : this.ignoredUnknownProperties) {
-                    if (pvs.contains(propName) && !wrapper.isWritableProperty(propName)) {
-                        pvs.removePropertyValue(propName);
-                    }
-                }
-                wrapper.setPropertyValues(pvs);
-            }
-        }
-        return job;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/TaskLogic.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/TaskLogic.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/TaskLogic.java
index 683cda6..f97bc83 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/TaskLogic.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/TaskLogic.java
@@ -51,7 +51,7 @@ import org.apache.syncope.server.provisioning.api.job.JobNamer;
 import org.apache.syncope.server.provisioning.api.job.TaskJob;
 import org.apache.syncope.server.provisioning.api.propagation.PropagationTaskExecutor;
 import org.apache.syncope.server.logic.init.ImplementationClassNamesLoader;
-import org.apache.syncope.server.logic.init.JobInstanceLoader;
+import org.apache.syncope.server.provisioning.api.job.JobInstanceLoader;
 import org.apache.syncope.server.logic.notification.NotificationJob;
 import org.quartz.JobDataMap;
 import org.quartz.JobKey;
@@ -305,6 +305,7 @@ public class TaskLogic extends AbstractTransactionalLogic<AbstractTaskTO> {
         if (TaskType.SCHEDULED == taskUtil.getType()
                 || TaskType.SYNCHRONIZATION == taskUtil.getType()
                 || TaskType.PUSH == taskUtil.getType()) {
+
             jobInstanceLoader.unregisterJob(task);
         }
 
@@ -373,9 +374,6 @@ public class TaskLogic extends AbstractTransactionalLogic<AbstractTaskTO> {
         return res;
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     protected AbstractTaskTO resolveReference(final Method method, final Object... args)
             throws UnresolvedReferenceException {

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/UserLogic.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/UserLogic.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/UserLogic.java
index c3b01f6..3e0de7e 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/UserLogic.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/UserLogic.java
@@ -339,7 +339,7 @@ public class UserLogic extends AbstractSubjectLogic<UserTO, UserMod> {
     public UserTO delete(final Long key) {
         List<Role> ownedRoles = roleDAO.findOwnedByUser(key);
         if (!ownedRoles.isEmpty()) {
-            List<String> owned = new ArrayList<String>(ownedRoles.size());
+            List<String> owned = new ArrayList<>(ownedRoles.size());
             for (Role role : ownedRoles) {
                 owned.add(role.getKey() + " " + role.getName());
             }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/JobInstanceLoader.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/JobInstanceLoader.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/JobInstanceLoader.java
deleted file mode 100644
index 3f6f753..0000000
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/JobInstanceLoader.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.server.logic.init;
-
-import java.text.ParseException;
-import org.apache.syncope.server.persistence.api.entity.Report;
-import org.apache.syncope.server.persistence.api.entity.task.Task;
-import org.quartz.SchedulerException;
-
-public interface JobInstanceLoader {
-
-    void registerJob(Task task, String jobClassName, String cronExpression)
-            throws ClassNotFoundException, SchedulerException, ParseException;
-
-    void registerJob(Report report) throws SchedulerException, ParseException;
-
-    void registerReportJob(Long reportKey) throws SchedulerException, ParseException;
-
-    void registerTaskJob(Long taskKey) throws ClassNotFoundException, SchedulerException, ParseException;
-
-    void unregisterJob(Task task);
-
-    void unregisterJob(Report report);
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/JobInstanceLoaderImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/JobInstanceLoaderImpl.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/JobInstanceLoaderImpl.java
index 48e7db4..e1d4c7d 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/JobInstanceLoaderImpl.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/JobInstanceLoaderImpl.java
@@ -35,6 +35,7 @@ import org.apache.syncope.server.persistence.api.entity.task.PushTask;
 import org.apache.syncope.server.persistence.api.entity.task.SchedTask;
 import org.apache.syncope.server.persistence.api.entity.task.SyncTask;
 import org.apache.syncope.server.persistence.api.entity.task.Task;
+import org.apache.syncope.server.provisioning.api.job.JobInstanceLoader;
 import org.apache.syncope.server.provisioning.api.job.JobNamer;
 import org.apache.syncope.server.provisioning.api.job.SyncJob;
 import org.apache.syncope.server.provisioning.api.job.TaskJob;

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/RoleReportlet.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/RoleReportlet.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/RoleReportlet.java
index 756f817..a6f698a 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/RoleReportlet.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/RoleReportlet.java
@@ -110,7 +110,7 @@ public class RoleReportlet extends AbstractReportlet<RoleReportletConf> {
 
         AttributesImpl atts = new AttributesImpl();
         if (!attrs.isEmpty()) {
-            Map<String, AttrTO> attrMap = attributableTO.getAttrMap();
+            Map<String, AttrTO> attrMap = attributableTO.getPlainAttrMap();
 
             handler.startElement("", "", "attributes", null);
             for (String attrName : attrs) {
@@ -233,7 +233,7 @@ public class RoleReportlet extends AbstractReportlet<RoleReportletConf> {
             // values to String is already encapsulated there
             RoleTO roleTO = roleDataBinder.getRoleTO(role);
 
-            doExtractAttributes(handler, roleTO, conf.getAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
+            doExtractAttributes(handler, roleTO, conf.getPlainAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
 
             if (conf.getFeatures().contains(Feature.entitlements)) {
                 handler.startElement("", "", "entitlements", null);
@@ -292,7 +292,7 @@ public class RoleReportlet extends AbstractReportlet<RoleReportletConf> {
             handler.endElement("", "", "feature");
         }
 
-        for (String attr : conf.getAttrs()) {
+        for (String attr : conf.getPlainAttrs()) {
             atts.clear();
             handler.startElement("", "", "attribute", atts);
             handler.characters(attr.toCharArray(), 0, attr.length());

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/UserReportlet.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/UserReportlet.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/UserReportlet.java
index ebdad43..61b7cd8 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/UserReportlet.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/UserReportlet.java
@@ -117,7 +117,7 @@ public class UserReportlet extends AbstractReportlet<UserReportletConf> {
 
         AttributesImpl atts = new AttributesImpl();
         if (!attrs.isEmpty()) {
-            Map<String, AttrTO> attrMap = attributableTO.getAttrMap();
+            Map<String, AttrTO> attrMap = attributableTO.getPlainAttrMap();
 
             handler.startElement("", "", "attributes", null);
             for (String attrName : attrs) {
@@ -271,7 +271,7 @@ public class UserReportlet extends AbstractReportlet<UserReportletConf> {
             // values to String is already encapsulated there
             UserTO userTO = userDataBinder.getUserTO(user);
 
-            doExtractAttributes(handler, userTO, conf.getAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
+            doExtractAttributes(handler, userTO, conf.getPlainAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
 
             if (conf.getFeatures().contains(Feature.memberships)) {
                 handler.startElement("", "", "memberships", null);
@@ -284,7 +284,7 @@ public class UserReportlet extends AbstractReportlet<UserReportletConf> {
                     atts.addAttribute("", "", "roleName", ReportXMLConst.XSD_STRING, String.valueOf(memb.getRoleName()));
                     handler.startElement("", "", "membership", atts);
 
-                    doExtractAttributes(handler, memb, memb.getAttrMap().keySet(), memb.getDerAttrMap()
+                    doExtractAttributes(handler, memb, memb.getPlainAttrMap().keySet(), memb.getDerAttrMap()
                             .keySet(), memb.getVirAttrMap().keySet());
 
                     if (conf.getFeatures().contains(Feature.resources)) {
@@ -324,7 +324,7 @@ public class UserReportlet extends AbstractReportlet<UserReportletConf> {
             handler.endElement("", "", "feature");
         }
 
-        for (String attr : conf.getAttrs()) {
+        for (String attr : conf.getPlainAttrs()) {
             atts.clear();
             handler.startElement("", "", "attribute", atts);
             handler.characters(attr.toCharArray(), 0, attr.length());

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/logic/src/test/java/org/apache/syncope/server/logic/NotificationTest.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/test/java/org/apache/syncope/server/logic/NotificationTest.java b/syncope620/server/logic/src/test/java/org/apache/syncope/server/logic/NotificationTest.java
index de655d8..83a238f 100644
--- a/syncope620/server/logic/src/test/java/org/apache/syncope/server/logic/NotificationTest.java
+++ b/syncope620/server/logic/src/test/java/org/apache/syncope/server/logic/NotificationTest.java
@@ -262,7 +262,7 @@ public class NotificationTest {
         notification.setSelfAsRecipient(true);
 
         notification.setRecipientAttrName("email");
-        notification.setRecipientAttrType(IntMappingType.UserSchema);
+        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 
         Random random = new Random(System.currentTimeMillis());
         String sender = "syncopetest-" + random.nextLong() + "@syncope.apache.org";
@@ -320,7 +320,7 @@ public class NotificationTest {
         notification.setSelfAsRecipient(true);
 
         notification.setRecipientAttrName("email");
-        notification.setRecipientAttrType(IntMappingType.UserSchema);
+        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 
         Random random = new Random(System.currentTimeMillis());
         String sender = "syncope192-" + random.nextLong() + "@syncope.apache.org";
@@ -372,7 +372,7 @@ public class NotificationTest {
         notification.setSelfAsRecipient(true);
 
         notification.setRecipientAttrName("email");
-        notification.setRecipientAttrType(IntMappingType.UserSchema);
+        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 
         Random random = new Random(System.currentTimeMillis());
         String sender = "syncopetest-" + random.nextLong() + "@syncope.apache.org";
@@ -422,7 +422,7 @@ public class NotificationTest {
         notification.setSelfAsRecipient(true);
 
         notification.setRecipientAttrName("email");
-        notification.setRecipientAttrType(IntMappingType.UserSchema);
+        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 
         Random random = new Random(System.currentTimeMillis());
         String sender = "syncopetest-" + random.nextLong() + "@syncope.apache.org";
@@ -491,7 +491,7 @@ public class NotificationTest {
         notification.setSelfAsRecipient(true);
 
         notification.setRecipientAttrName("email");
-        notification.setRecipientAttrType(IntMappingType.UserSchema);
+        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 
         notification.getStaticRecipients().add("syncope445@syncope.apache.org");
 
@@ -549,7 +549,7 @@ public class NotificationTest {
         notification.setSelfAsRecipient(true);
 
         notification.setRecipientAttrName("email");
-        notification.setRecipientAttrType(IntMappingType.UserSchema);
+        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 
         notification.getStaticRecipients().add("syncope492@syncope.apache.org");
 
@@ -593,7 +593,7 @@ public class NotificationTest {
         notification.setSelfAsRecipient(false);
 
         notification.setRecipientAttrName("email");
-        notification.setRecipientAttrType(IntMappingType.UserSchema);
+        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 
         notification.getStaticRecipients().add(MAIL_ADDRESS);
 
@@ -635,7 +635,7 @@ public class NotificationTest {
 
         assertNotNull(taskId);
         assertNotNull(textBody);
-        assertTrue(recipients.contains(MAIL_ADDRESS));
+        assertTrue(recipients != null && recipients.contains(MAIL_ADDRESS));
 
         // 5. execute Notification task and verify e-mail
         taskLogic.execute(taskId, false);

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/logic/src/test/resources/logicTest.xml
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/test/resources/logicTest.xml b/syncope620/server/logic/src/test/resources/logicTest.xml
index d3b1bd8..afb8730 100644
--- a/syncope620/server/logic/src/test/resources/logicTest.xml
+++ b/syncope620/server/logic/src/test/resources/logicTest.xml
@@ -41,5 +41,5 @@ under the License.
     <property name="primary" value="file:${conf.directory}/content.xml"/>
     <property name="fallback" value="classpath:content.xml"/>
   </bean>
-  
+
 </beans>

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/ConnObjectUtil.java
----------------------------------------------------------------------
diff --git a/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/ConnObjectUtil.java b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/ConnObjectUtil.java
index 831a981..592cd0c 100644
--- a/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/ConnObjectUtil.java
+++ b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/ConnObjectUtil.java
@@ -304,8 +304,8 @@ public class ConnObjectUtil {
                     }
                     break;
 
-                case UserSchema:
-                case RoleSchema:
+                case UserPlainSchema:
+                case RolePlainSchema:
                     attributeTO = new AttrTO();
                     attributeTO.setSchema(item.getIntAttrName());
 
@@ -446,7 +446,7 @@ public class ConnObjectUtil {
 
                 ((RoleTO) subjectTO).setInheritOwner(((RoleTO) template).isInheritOwner());
                 ((RoleTO) subjectTO).setInheritTemplates(((RoleTO) template).isInheritTemplates());
-                ((RoleTO) subjectTO).setInheritAttrs(((RoleTO) template).isInheritAttrs());
+                ((RoleTO) subjectTO).setInheritPlainAttrs(((RoleTO) template).isInheritPlainAttrs());
                 ((RoleTO) subjectTO).setInheritDerAttrs(((RoleTO) template).isInheritDerAttrs());
                 ((RoleTO) subjectTO).setInheritVirAttrs(((RoleTO) template).isInheritVirAttrs());
                 ((RoleTO) subjectTO).setInheritPasswordPolicy(((RoleTO) template).isInheritPasswordPolicy());
@@ -700,7 +700,7 @@ public class ConnObjectUtil {
     }
 
     private void fillFromTemplate(final AbstractAttributableTO attributableTO, final AbstractAttributableTO template) {
-        Map<String, AttrTO> currentAttrMap = attributableTO.getAttrMap();
+        Map<String, AttrTO> currentAttrMap = attributableTO.getPlainAttrMap();
         for (AttrTO templateAttr : template.getPlainAttrs()) {
             if (templateAttr.getValues() != null && !templateAttr.getValues().isEmpty()
                     && (!currentAttrMap.containsKey(templateAttr.getSchema())

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/MappingUtil.java
----------------------------------------------------------------------
diff --git a/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/MappingUtil.java b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/MappingUtil.java
index 7ae586f..209c800 100644
--- a/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/MappingUtil.java
+++ b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/MappingUtil.java
@@ -303,9 +303,9 @@ public final class MappingUtil {
         final Map.Entry<String, Attribute> result;
 
         switch (mapItem.getIntMappingType()) {
-            case UserSchema:
-            case RoleSchema:
-            case MembershipSchema:
+            case UserPlainSchema:
+            case RolePlainSchema:
+            case MembershipPlainSchema:
                 final PlainSchemaDAO plainSchemaDAO = context.getBean(PlainSchemaDAO.class);
                 schema = plainSchemaDAO.find(mapItem.getIntAttrName(),
                         MappingUtil.getIntMappingTypeClass(mapItem.getIntMappingType()));
@@ -487,9 +487,9 @@ public final class MappingUtil {
         List<PlainAttrValue> values = new ArrayList<>();
         PlainAttrValue attrValue;
         switch (mappingItem.getIntMappingType()) {
-            case UserSchema:
-            case RoleSchema:
-            case MembershipSchema:
+            case UserPlainSchema:
+            case RolePlainSchema:
+            case MembershipPlainSchema:
                 for (Attributable<?, ?, ?> attributable : attributables) {
                     final PlainAttr attr = attributable.getPlainAttr(mappingItem.getIntAttrName());
                     if (attr != null) {
@@ -687,15 +687,15 @@ public final class MappingUtil {
         Class result;
 
         switch (intMappingType) {
-            case UserSchema:
+            case UserPlainSchema:
                 result = UPlainSchema.class;
                 break;
 
-            case RoleSchema:
+            case RolePlainSchema:
                 result = RPlainSchema.class;
                 break;
 
-            case MembershipSchema:
+            case MembershipPlainSchema:
                 result = MPlainSchema.class;
                 break;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/policy/AccountPolicyEnforcer.java
----------------------------------------------------------------------
diff --git a/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/policy/AccountPolicyEnforcer.java b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/policy/AccountPolicyEnforcer.java
index f850b98..002a588 100644
--- a/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/policy/AccountPolicyEnforcer.java
+++ b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/policy/AccountPolicyEnforcer.java
@@ -22,6 +22,7 @@ import java.util.regex.Pattern;
 import org.apache.syncope.common.lib.types.AccountPolicySpec;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.server.persistence.api.entity.user.User;
+import org.apache.syncope.server.provisioning.api.UserSuspender;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/policy/PasswordPolicyEnforcer.java
----------------------------------------------------------------------
diff --git a/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/policy/PasswordPolicyEnforcer.java b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/policy/PasswordPolicyEnforcer.java
index ac8513d..8bbc734 100644
--- a/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/policy/PasswordPolicyEnforcer.java
+++ b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/policy/PasswordPolicyEnforcer.java
@@ -26,11 +26,6 @@ import org.springframework.stereotype.Component;
 @Component
 public class PasswordPolicyEnforcer implements PolicyEnforcer<PasswordPolicySpec, User> {
 
-    /* (non-Javadoc)
-     * @see
-     * org.apache.syncope.core.policy.PasswordPolicyEnforcer#enforce(org.apache.syncope.common.types.PasswordPolicySpec,
-     * org.apache.syncope.common.types.PolicyType, java.lang.String)
-     */
     @Override
     public void enforce(final PasswordPolicySpec policy, final PolicyType type, final User user)
             throws PasswordPolicyException, PolicyEnforceException {
@@ -43,7 +38,7 @@ public class PasswordPolicyEnforcer implements PolicyEnforcer<PasswordPolicySpec
         }
 
         if (password == null && !policy.isAllowNullPassword()) {
-            throw new PolicyEnforceException("Password must not be null and must be stored internally");
+            throw new PolicyEnforceException("Password mandatory");
         } else if (password != null && clearPassword != null) {
             // check length
             if (policy.getMinLength() > 0 && policy.getMinLength() > clearPassword.length()) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/policy/UserSuspender.java
----------------------------------------------------------------------
diff --git a/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/policy/UserSuspender.java b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/policy/UserSuspender.java
deleted file mode 100644
index 1fbf6f6..0000000
--- a/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/policy/UserSuspender.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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.server.misc.policy;
-
-import org.apache.syncope.server.persistence.api.entity.user.User;
-
-public interface UserSuspender {
-
-    void suspend(User user, boolean propagateSuspension);
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/security/AuthContextUtil.java
----------------------------------------------------------------------
diff --git a/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/security/AuthContextUtil.java b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/security/AuthContextUtil.java
index e58a157..870b444 100644
--- a/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/security/AuthContextUtil.java
+++ b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/security/AuthContextUtil.java
@@ -59,7 +59,7 @@ public final class AuthContextUtil {
      */
     public static void extendAuthContext(final Long roleKey, final String roleEntitlement) {
         Authentication auth = SecurityContextHolder.getContext().getAuthentication();
-        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(auth.getAuthorities());
+        List<GrantedAuthority> authorities = new ArrayList<>(auth.getAuthorities());
         authorities.add(new SimpleGrantedAuthority(roleEntitlement));
         Authentication newAuth = new UsernamePasswordAuthenticationToken(
                 auth.getPrincipal(), auth.getCredentials(), authorities);

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/content/XMLContentExporter.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/content/XMLContentExporter.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/content/XMLContentExporter.java
index 90bbbee..3456431 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/content/XMLContentExporter.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/content/XMLContentExporter.java
@@ -86,6 +86,9 @@ public class XMLContentExporter extends AbstractContentDealer implements Content
                 JPAMDerAttr.TABLE, JPAMVirAttr.TABLE
             }));
 
+    protected final static Set<String> TABLE_SUFFIXES_TO_BE_INCLUDED =
+            new HashSet<>(Arrays.asList(new String[] { "TEMPLATE" }));
+
     protected static final Map<String, String> TABLES_TO_BE_FILTERED =
             Collections.singletonMap("TASK", "DTYPE <> 'PropagationTask'");
 
@@ -96,7 +99,11 @@ public class XMLContentExporter extends AbstractContentDealer implements Content
         boolean allowed = true;
         for (String prefix : TABLE_PREFIXES_TO_BE_EXCLUDED) {
             if (tableName.toUpperCase().startsWith(prefix)) {
-                allowed = false;
+                for (String suffix : TABLE_SUFFIXES_TO_BE_INCLUDED) {
+                    if (!tableName.toUpperCase().endsWith(suffix)) {
+                        allowed = false;
+                    }
+                }
             }
         }
         return allowed;

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPASubjectSearchDAO.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPASubjectSearchDAO.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPASubjectSearchDAO.java
index f4475f8..36d48e7 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPASubjectSearchDAO.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPASubjectSearchDAO.java
@@ -20,6 +20,7 @@ package org.apache.syncope.server.persistence.jpa.dao;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
@@ -31,6 +32,7 @@ import javax.persistence.TemporalType;
 import javax.validation.ValidationException;
 import javax.validation.constraints.Max;
 import javax.validation.constraints.Min;
+import org.apache.commons.lang3.ClassUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.syncope.common.lib.types.SubjectType;
@@ -50,7 +52,6 @@ import org.apache.syncope.server.persistence.api.entity.AttributableUtilFactory;
 import org.apache.syncope.server.persistence.api.entity.PlainAttrValue;
 import org.apache.syncope.server.persistence.api.entity.PlainSchema;
 import org.apache.syncope.server.persistence.api.entity.Subject;
-import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Repository;
 import org.springframework.util.ReflectionUtils;
@@ -652,6 +653,11 @@ public class JPASubjectSearchDAO extends AbstractDAO<Subject<?, ?, ?>, Long> imp
 
         final AttributableUtil attrUtil = attrUtilFactory.getInstance(type.asAttributableType());
 
+        // Keeps track of difference between entity's getKey() and JPA @Id fields
+        if ("key".equals(cond.getSchema())) {
+            cond.setSchema("id");
+        }
+
         Field subjectField = ReflectionUtils.findField(attrUtil.attributableClass(), cond.getSchema());
         if (subjectField == null) {
             LOG.warn("Ignoring invalid schema '{}'", cond.getSchema());
@@ -685,13 +691,22 @@ public class JPASubjectSearchDAO extends AbstractDAO<Subject<?, ?, ?>, Long> imp
 
         // Deal with subject fields representing relationships to other entities
         if (subjectField.getType().getAnnotation(Entity.class) != null) {
-            if (BeanUtils.findDeclaredMethodWithMinimalParameters(subjectField.getType(), "getId") != null) {
-                cond.setSchema(cond.getSchema() + "_id");
-                schema.setType(AttrSchemaType.Long);
+            Method relMethod = null;
+            try {
+                relMethod = ClassUtils.getPublicMethod(subjectField.getType(), "getKey", new Class[0]);
+            } catch (Exception e) {
+                LOG.error("Could not find {}#getKey", subjectField.getType(), e);
             }
-            if (BeanUtils.findDeclaredMethodWithMinimalParameters(subjectField.getType(), "getName") != null) {
-                cond.setSchema(cond.getSchema() + "_name");
-                schema.setType(AttrSchemaType.String);
+
+            if (relMethod != null) {
+                if (Long.class.isAssignableFrom(relMethod.getReturnType())) {
+                    cond.setSchema(cond.getSchema() + "_id");
+                    schema.setType(AttrSchemaType.Long);
+                }
+                if (String.class.isAssignableFrom(relMethod.getReturnType())) {
+                    cond.setSchema(cond.getSchema() + "_name");
+                    schema.setType(AttrSchemaType.String);
+                }
             }
         }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPATaskDAO.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPATaskDAO.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPATaskDAO.java
index 954697c..e0d2313 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPATaskDAO.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPATaskDAO.java
@@ -144,7 +144,7 @@ public class JPATaskDAO extends AbstractDAO<Task, Long> implements TaskDAO {
     @Override
     public int count(final TaskType type) {
         Query countQuery = entityManager.createNativeQuery("SELECT COUNT(id) FROM Task WHERE TYPE=?1");
-        countQuery.setParameter(1, type.toString());
+        countQuery.setParameter(1, type.name());
         return ((Number) countQuery.getSingleResult()).intValue();
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPAUserDAO.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPAUserDAO.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPAUserDAO.java
index 30fdca5..2a45ad3 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPAUserDAO.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPAUserDAO.java
@@ -51,6 +51,7 @@ import org.apache.syncope.server.misc.security.AuthContextUtil;
 import org.apache.syncope.server.misc.security.UnauthorizedRoleException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
 
 @Repository
 public class JPAUserDAO extends AbstractSubjectDAO<UPlainAttr, UDerAttr, UVirAttr> implements UserDAO {
@@ -249,6 +250,7 @@ public class JPAUserDAO extends AbstractSubjectDAO<UPlainAttr, UDerAttr, UVirAtt
         }
     }
 
+    @Transactional(readOnly = true)
     @Override
     public User authFetch(final Long key) {
         if (key == null) {
@@ -265,6 +267,7 @@ public class JPAUserDAO extends AbstractSubjectDAO<UPlainAttr, UDerAttr, UVirAtt
         return user;
     }
 
+    @Transactional(readOnly = true)
     @Override
     public User authFetch(final String username) {
         if (username == null) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractAttrTemplate.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractAttrTemplate.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractAttrTemplate.java
index 3c79c92..fe78889 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractAttrTemplate.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractAttrTemplate.java
@@ -18,25 +18,11 @@
  */
 package org.apache.syncope.server.persistence.jpa.entity;
 
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.MappedSuperclass;
 import org.apache.syncope.server.persistence.api.entity.AttrTemplate;
 import org.apache.syncope.server.persistence.api.entity.Schema;
 
-@MappedSuperclass
 public abstract class AbstractAttrTemplate<S extends Schema> extends AbstractEntity<Long> implements AttrTemplate<S> {
 
     private static final long serialVersionUID = 4829112252713766666L;
 
-    @Id
-    @GeneratedValue(strategy = GenerationType.AUTO)
-    protected Long id;
-
-    @Override
-    public Long getKey() {
-        return id;
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractDerAttrTemplate.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractDerAttrTemplate.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractDerAttrTemplate.java
new file mode 100644
index 0000000..2dec16a
--- /dev/null
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractDerAttrTemplate.java
@@ -0,0 +1,41 @@
+/*
+ * 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.server.persistence.jpa.entity;
+
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+import org.apache.syncope.server.persistence.api.entity.DerSchema;
+
+@MappedSuperclass
+public abstract class AbstractDerAttrTemplate<D extends DerSchema> extends AbstractAttrTemplate<D> {
+
+    private static final long serialVersionUID = 8871895736733379865L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    protected Long id;
+
+    @Override
+    public Long getKey() {
+        return id;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractPlainAttrTemplate.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractPlainAttrTemplate.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractPlainAttrTemplate.java
new file mode 100644
index 0000000..f8b0150
--- /dev/null
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractPlainAttrTemplate.java
@@ -0,0 +1,27 @@
+/*
+ * 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.server.persistence.jpa.entity;
+
+import org.apache.syncope.server.persistence.api.entity.PlainSchema;
+
+public abstract class AbstractPlainAttrTemplate<P extends PlainSchema> extends AbstractAttrTemplate<P> {
+
+    private static final long serialVersionUID = -943169893494860655L;
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractPlainSchema.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractPlainSchema.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractPlainSchema.java
index ecc6fd5..78356bf 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractPlainSchema.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractPlainSchema.java
@@ -29,6 +29,7 @@ import javax.persistence.MappedSuperclass;
 import javax.persistence.Transient;
 import javax.validation.constraints.Max;
 import javax.validation.constraints.Min;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
 import org.apache.syncope.server.persistence.api.attrvalue.validation.Validator;
@@ -231,7 +232,7 @@ public abstract class AbstractPlainSchema extends AbstractEntity<String> impleme
 
     @Override
     public void setConversionPattern(final String conversionPattern) {
-        if (!getType().isConversionPatternNeeded()) {
+        if (StringUtils.isNotBlank(conversionPattern) && !getType().isConversionPatternNeeded()) {
             LOG.warn("Conversion pattern will be ignored: this attribute type is {}", getType());
         }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractVirAttrTemplate.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractVirAttrTemplate.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractVirAttrTemplate.java
new file mode 100644
index 0000000..5a3b2e4
--- /dev/null
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/AbstractVirAttrTemplate.java
@@ -0,0 +1,41 @@
+/*
+ * 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.server.persistence.jpa.entity;
+
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+import org.apache.syncope.server.persistence.api.entity.VirSchema;
+
+@MappedSuperclass
+public abstract class AbstractVirAttrTemplate<V extends VirSchema> extends AbstractAttrTemplate<V> {
+
+    private static final long serialVersionUID = -943169893494860655L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    protected Long id;
+
+    @Override
+    public Long getKey() {
+        return id;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/JPAAttributableUtil.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/JPAAttributableUtil.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/JPAAttributableUtil.java
index bf468ce..94f95a8 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/JPAAttributableUtil.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/JPAAttributableUtil.java
@@ -744,15 +744,15 @@ public class JPAAttributableUtil implements AttributableUtil {
 
         switch (type) {
             case ROLE:
-                result = IntMappingType.RoleSchema;
+                result = IntMappingType.RolePlainSchema;
                 break;
 
             case MEMBERSHIP:
-                result = IntMappingType.MembershipSchema;
+                result = IntMappingType.MembershipPlainSchema;
                 break;
 
             case USER:
-                result = IntMappingType.UserSchema;
+                result = IntMappingType.UserPlainSchema;
                 break;
 
             case CONFIGURATION:
@@ -768,15 +768,15 @@ public class JPAAttributableUtil implements AttributableUtil {
 
         switch (type) {
             case ROLE:
-                result = IntMappingType.RoleSchema;
+                result = IntMappingType.RoleDerivedSchema;
                 break;
 
             case MEMBERSHIP:
-                result = IntMappingType.MembershipSchema;
+                result = IntMappingType.MembershipDerivedSchema;
                 break;
 
             case USER:
-                result = IntMappingType.UserSchema;
+                result = IntMappingType.UserDerivedSchema;
                 break;
 
             case CONFIGURATION:

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/JPAConnInstance.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/JPAConnInstance.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/JPAConnInstance.java
index 2c1e956..28e1c87 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/JPAConnInstance.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/JPAConnInstance.java
@@ -186,7 +186,7 @@ public class JPAConnInstance extends AbstractEntity<Long> implements ConnInstanc
         if (!StringUtils.isBlank(jsonConf)) {
             ConnConfProperty[] deserialized = POJOHelper.deserialize(jsonConf, ConnConfProperty[].class);
             if (ArrayUtils.isNotEmpty(deserialized)) {
-                configuration = new HashSet<ConnConfProperty>(Arrays.asList(deserialized));
+                configuration = new HashSet<>(Arrays.asList(deserialized));
             }
         }
 
@@ -195,7 +195,7 @@ public class JPAConnInstance extends AbstractEntity<Long> implements ConnInstanc
 
     @Override
     public void setConfiguration(final Set<ConnConfProperty> configuration) {
-        jsonConf = POJOHelper.serialize(new HashSet<ConnConfProperty>(configuration));
+        jsonConf = POJOHelper.serialize(new HashSet<>(configuration));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/JPAReport.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/JPAReport.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/JPAReport.java
index d045bcf..8b01c60 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/JPAReport.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/JPAReport.java
@@ -113,8 +113,6 @@ public class JPAReport extends AbstractEntity<Long> implements Report {
             return false;
         }
 
-        checkType(reportletConf, JPAReportletConfInstance.class);
-
         JPAReportletConfInstance found = null;
         for (JPAReportletConfInstance instance : reportletConfs) {
             if (reportletConf.equals(instance.getInstance())) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/membership/JPAMDerAttrTemplate.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/membership/JPAMDerAttrTemplate.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/membership/JPAMDerAttrTemplate.java
index a106453..f52627c 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/membership/JPAMDerAttrTemplate.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/membership/JPAMDerAttrTemplate.java
@@ -25,12 +25,12 @@ import javax.persistence.Table;
 import org.apache.syncope.server.persistence.api.entity.membership.MDerAttrTemplate;
 import org.apache.syncope.server.persistence.api.entity.membership.MDerSchema;
 import org.apache.syncope.server.persistence.api.entity.role.Role;
-import org.apache.syncope.server.persistence.jpa.entity.AbstractAttrTemplate;
+import org.apache.syncope.server.persistence.jpa.entity.AbstractDerAttrTemplate;
 import org.apache.syncope.server.persistence.jpa.entity.role.JPARole;
 
 @Entity
 @Table(name = JPAMDerAttrTemplate.TABLE)
-public class JPAMDerAttrTemplate extends AbstractAttrTemplate<MDerSchema> implements MDerAttrTemplate {
+public class JPAMDerAttrTemplate extends AbstractDerAttrTemplate<MDerSchema> implements MDerAttrTemplate {
 
     private static final long serialVersionUID = -4465930976210263434L;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/membership/JPAMPlainAttrTemplate.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/membership/JPAMPlainAttrTemplate.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/membership/JPAMPlainAttrTemplate.java
index 5fdf9ec..5f33b8c 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/membership/JPAMPlainAttrTemplate.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/membership/JPAMPlainAttrTemplate.java
@@ -19,23 +19,27 @@
 package org.apache.syncope.server.persistence.jpa.entity.membership;
 
 import javax.persistence.Entity;
+import javax.persistence.Id;
 import javax.persistence.JoinColumn;
 import javax.persistence.ManyToOne;
 import javax.persistence.Table;
 import org.apache.syncope.server.persistence.api.entity.membership.MPlainAttrTemplate;
 import org.apache.syncope.server.persistence.api.entity.membership.MPlainSchema;
 import org.apache.syncope.server.persistence.api.entity.role.Role;
-import org.apache.syncope.server.persistence.jpa.entity.AbstractAttrTemplate;
+import org.apache.syncope.server.persistence.jpa.entity.AbstractPlainAttrTemplate;
 import org.apache.syncope.server.persistence.jpa.entity.role.JPARole;
 
 @Entity
 @Table(name = JPAMPlainAttrTemplate.TABLE)
-public class JPAMPlainAttrTemplate extends AbstractAttrTemplate<MPlainSchema> implements MPlainAttrTemplate {
+public class JPAMPlainAttrTemplate extends AbstractPlainAttrTemplate<MPlainSchema> implements MPlainAttrTemplate {
 
     private static final long serialVersionUID = -8768086609963244514L;
 
     public static final String TABLE = "MPlainAttrTemplate";
 
+    @Id
+    private Long id;
+
     @ManyToOne
     private JPARole owner;
 
@@ -44,6 +48,11 @@ public class JPAMPlainAttrTemplate extends AbstractAttrTemplate<MPlainSchema> im
     private JPAMPlainSchema schema;
 
     @Override
+    public Long getKey() {
+        return id;
+    }
+
+    @Override
     public MPlainSchema getSchema() {
         return schema;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/membership/JPAMVirAttrTemplate.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/membership/JPAMVirAttrTemplate.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/membership/JPAMVirAttrTemplate.java
index 241d28c..1d7e184 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/membership/JPAMVirAttrTemplate.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/membership/JPAMVirAttrTemplate.java
@@ -25,12 +25,12 @@ import javax.persistence.Table;
 import org.apache.syncope.server.persistence.api.entity.membership.MVirAttrTemplate;
 import org.apache.syncope.server.persistence.api.entity.membership.MVirSchema;
 import org.apache.syncope.server.persistence.api.entity.role.Role;
-import org.apache.syncope.server.persistence.jpa.entity.AbstractAttrTemplate;
+import org.apache.syncope.server.persistence.jpa.entity.AbstractVirAttrTemplate;
 import org.apache.syncope.server.persistence.jpa.entity.role.JPARole;
 
 @Entity
 @Table(name = JPAMVirAttrTemplate.TABLE)
-public class JPAMVirAttrTemplate extends AbstractAttrTemplate<MVirSchema> implements MVirAttrTemplate {
+public class JPAMVirAttrTemplate extends AbstractVirAttrTemplate<MVirSchema> implements MVirAttrTemplate {
 
     private static final long serialVersionUID = 6618560912535667392L;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/role/JPARDerAttrTemplate.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/role/JPARDerAttrTemplate.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/role/JPARDerAttrTemplate.java
index 3686f48..7ddc20f 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/role/JPARDerAttrTemplate.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/role/JPARDerAttrTemplate.java
@@ -25,11 +25,11 @@ import javax.persistence.Table;
 import org.apache.syncope.server.persistence.api.entity.role.RDerAttrTemplate;
 import org.apache.syncope.server.persistence.api.entity.role.RDerSchema;
 import org.apache.syncope.server.persistence.api.entity.role.Role;
-import org.apache.syncope.server.persistence.jpa.entity.AbstractAttrTemplate;
+import org.apache.syncope.server.persistence.jpa.entity.AbstractDerAttrTemplate;
 
 @Entity
 @Table(name = JPARDerAttrTemplate.TABLE)
-public class JPARDerAttrTemplate extends AbstractAttrTemplate<RDerSchema> implements RDerAttrTemplate {
+public class JPARDerAttrTemplate extends AbstractDerAttrTemplate<RDerSchema> implements RDerAttrTemplate {
 
     private static final long serialVersionUID = 624868884107016649L;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/role/JPARPlainAttrTemplate.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/role/JPARPlainAttrTemplate.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/role/JPARPlainAttrTemplate.java
index 9c0e603..fa6e8ee 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/role/JPARPlainAttrTemplate.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/role/JPARPlainAttrTemplate.java
@@ -19,25 +19,34 @@
 package org.apache.syncope.server.persistence.jpa.entity.role;
 
 import javax.persistence.Entity;
+import javax.persistence.Id;
 import javax.persistence.JoinColumn;
 import javax.persistence.ManyToOne;
 import javax.persistence.Table;
 import org.apache.syncope.server.persistence.api.entity.role.RPlainAttrTemplate;
 import org.apache.syncope.server.persistence.api.entity.role.RPlainSchema;
 import org.apache.syncope.server.persistence.api.entity.role.Role;
-import org.apache.syncope.server.persistence.jpa.entity.AbstractAttrTemplate;
+import org.apache.syncope.server.persistence.jpa.entity.AbstractPlainAttrTemplate;
 
 @Entity
 @Table(name = JPARPlainAttrTemplate.TABLE)
-public class JPARPlainAttrTemplate extends AbstractAttrTemplate<RPlainSchema> implements RPlainAttrTemplate {
+public class JPARPlainAttrTemplate extends AbstractPlainAttrTemplate<RPlainSchema> implements RPlainAttrTemplate {
 
     private static final long serialVersionUID = 6943917051517266268L;
 
     public static final String TABLE = "RPlainAttrTemplate";
 
+    @Id
+    private Long id;
+
     @ManyToOne
     private JPARole owner;
 
+    @Override
+    public Long getKey() {
+        return id;
+    }
+
     @ManyToOne
     @JoinColumn(name = "schema_name")
     private JPARPlainSchema schema;

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/role/JPARVirAttrTemplate.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/role/JPARVirAttrTemplate.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/role/JPARVirAttrTemplate.java
index 1de733a..74200db 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/role/JPARVirAttrTemplate.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/role/JPARVirAttrTemplate.java
@@ -25,11 +25,11 @@ import javax.persistence.Table;
 import org.apache.syncope.server.persistence.api.entity.role.RVirAttrTemplate;
 import org.apache.syncope.server.persistence.api.entity.role.RVirSchema;
 import org.apache.syncope.server.persistence.api.entity.role.Role;
-import org.apache.syncope.server.persistence.jpa.entity.AbstractAttrTemplate;
+import org.apache.syncope.server.persistence.jpa.entity.AbstractVirAttrTemplate;
 
 @Entity
 @Table(name = JPARVirAttrTemplate.TABLE)
-public class JPARVirAttrTemplate extends AbstractAttrTemplate<RVirSchema> implements RVirAttrTemplate {
+public class JPARVirAttrTemplate extends AbstractVirAttrTemplate<RVirSchema> implements RVirAttrTemplate {
 
     private static final long serialVersionUID = 4896495904794493479L;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/task/JPATaskUtil.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/task/JPATaskUtil.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/task/JPATaskUtil.java
index 4086018..178d2b0 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/task/JPATaskUtil.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/task/JPATaskUtil.java
@@ -80,12 +80,33 @@ public final class JPATaskUtil implements TaskUtil {
 
     @Override
     public <T extends Task> T newTask() {
-        final Class<T> taskClass = taskClass();
-        try {
-            return taskClass == null ? null : taskClass.newInstance();
-        } catch (Exception e) {
-            return null;
+        T result = null;
+
+        switch (type) {
+            case PROPAGATION:
+                result = (T) new JPAPropagationTask();
+                break;
+
+            case SCHEDULED:
+                result = (T) new JPASchedTask();
+                break;
+
+            case SYNCHRONIZATION:
+                result = (T) new JPASyncTask();
+                break;
+
+            case PUSH:
+                result = (T) new JPAPushTask();
+                break;
+
+            case NOTIFICATION:
+                result = (T) new JPANotificationTask();
+                break;
+
+            default:
         }
+
+        return result;
     }
 
     @Override
@@ -119,7 +140,6 @@ public final class JPATaskUtil implements TaskUtil {
         return result;
     }
 
-    @SuppressWarnings("unchecked")
     @Override
     public <T extends AbstractTaskTO> T newTaskTO() {
         final Class<T> taskClass = taskTOClass();

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/validation/entity/EntityValidationListener.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/validation/entity/EntityValidationListener.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/validation/entity/EntityValidationListener.java
index bd40a4c..5f8c89d 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/validation/entity/EntityValidationListener.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/validation/entity/EntityValidationListener.java
@@ -23,8 +23,17 @@ import javax.persistence.PrePersist;
 import javax.persistence.PreUpdate;
 import javax.validation.ConstraintViolation;
 import javax.validation.Validator;
+import org.apache.commons.lang3.ClassUtils;
 import org.apache.syncope.server.persistence.api.attrvalue.validation.InvalidEntityException;
 import org.apache.syncope.server.misc.spring.ApplicationContextProvider;
+import org.apache.syncope.server.persistence.api.entity.AnnotatedEntity;
+import org.apache.syncope.server.persistence.api.entity.Attr;
+import org.apache.syncope.server.persistence.api.entity.Attributable;
+import org.apache.syncope.server.persistence.api.entity.Entity;
+import org.apache.syncope.server.persistence.api.entity.Policy;
+import org.apache.syncope.server.persistence.api.entity.Schema;
+import org.apache.syncope.server.persistence.api.entity.Subject;
+import org.apache.syncope.server.persistence.api.entity.task.Task;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -45,7 +54,24 @@ public class EntityValidationListener {
         Set<ConstraintViolation<Object>> violations = validator.validate(object);
         if (!violations.isEmpty()) {
             LOG.warn("Bean validation errors found: {}", violations);
-            throw new InvalidEntityException(object.getClass().getSimpleName(), violations);
+
+            Class<?> entityInt = null;
+            for (Class<?> interf : ClassUtils.getAllInterfaces(object.getClass())) {
+                if (!Entity.class.equals(interf)
+                        && !AnnotatedEntity.class.equals(interf)
+                        && !Schema.class.equals(interf)
+                        && !Attr.class.equals(interf)
+                        && !Task.class.equals(interf)
+                        && !Policy.class.equals(interf)
+                        && !Attributable.class.equals(interf)
+                        && !Subject.class.equals(interf)
+                        && Entity.class.isAssignableFrom(interf)) {
+
+                    entityInt = interf;
+                }
+            }
+
+            throw new InvalidEntityException(entityInt == null ? "Entity" : entityInt.getSimpleName(), violations);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/main/resources/META-INF/orm.xml
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/resources/META-INF/orm.xml b/syncope620/server/persistence-jpa/src/main/resources/META-INF/orm.xml
index 9a828d4..93bc24d 100644
--- a/syncope620/server/persistence-jpa/src/main/resources/META-INF/orm.xml
+++ b/syncope620/server/persistence-jpa/src/main/resources/META-INF/orm.xml
@@ -124,11 +124,27 @@ under the License.
       </id>
     </attributes>
   </entity>
+  <entity class="org.apache.syncope.server.persistence.jpa.entity.role.JPARPlainAttrTemplate">
+    <attributes>
+      <id name="id">
+        <generated-value generator="SEQ_RPlainAttrTemplate" strategy="TABLE"/>
+        <table-generator name="SEQ_RPlainAttrTemplate" pk-column-value="SEQ_RPlainAttrTemplate" initial-value="1000"/>
+      </id>
+    </attributes>
+  </entity>
   <entity class="org.apache.syncope.server.persistence.jpa.entity.membership.JPAMPlainAttr">
     <attributes>
       <id name="id">
-        <generated-value generator="SEQ_MAttrPlain" strategy="TABLE"/>
-        <table-generator name="SEQ_MAttrPlain" pk-column-value="SEQ_MAttrPlain" initial-value="1000"/>
+        <generated-value generator="SEQ_MPlainAttr" strategy="TABLE"/>
+        <table-generator name="SEQ_MPlainAttr" pk-column-value="SEQ_MPlainAttr" initial-value="1000"/>
+      </id>
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.server.persistence.jpa.entity.membership.JPAMPlainAttrTemplate">
+    <attributes>
+      <id name="id">
+        <generated-value generator="SEQ_MPlainAttrTemplate" strategy="TABLE"/>
+        <table-generator name="SEQ_MPlainAttrTemplate" pk-column-value="SEQ_MPlainAttrTemplate" initial-value="1000"/>
       </id>
     </attributes>
   </entity>

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/entity/AttributableSearchTest.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/entity/AttributableSearchTest.java b/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/entity/AttributableSearchTest.java
index 4b6345b..9eaa312 100644
--- a/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/entity/AttributableSearchTest.java
+++ b/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/entity/AttributableSearchTest.java
@@ -131,7 +131,7 @@ public class AttributableSearchTest extends AbstractTest {
         assertNotNull(users);
         assertEquals(4, users.size());
 
-        Set<Long> ids = new HashSet<Long>(users.size());
+        Set<Long> ids = new HashSet<>(users.size());
         for (User user : users) {
             ids.add(user.getKey());
         }
@@ -254,16 +254,30 @@ public class AttributableSearchTest extends AbstractTest {
     }
 
     @Test
-    public void searchByUsernameAndId() {
-        SubjectCond usernameLeafCond = new SubjectCond(SubjectCond.Type.EQ);
+    public void searchByBooleanSubjectCond() {
+        SubjectCond booleanCond = new SubjectCond(SubjectCond.Type.EQ);
+        booleanCond.setSchema("inheritPlainAttrs");
+        booleanCond.setExpression("true");
+
+        SearchCond searchCondition = SearchCond.getLeafCond(booleanCond);
+
+        List<Role> matchingRoles = searchDAO.search(RoleEntitlementUtil.getRoleKeys(entitlementDAO.findAll()),
+                searchCondition, SubjectType.ROLE);
+        assertNotNull(matchingRoles);
+        assertFalse(matchingRoles.isEmpty());
+    }
+
+    @Test
+    public void searchByUsernameAndKey() {
+        SubjectCond usernameLeafCond = new SubjectCond(SubjectCond.Type.LIKE);
         usernameLeafCond.setSchema("username");
-        usernameLeafCond.setExpression("rossini");
+        usernameLeafCond.setExpression("%ini");
 
         SubjectCond idRightCond = new SubjectCond(SubjectCond.Type.LT);
-        idRightCond.setSchema("id");
+        idRightCond.setSchema("key");
         idRightCond.setExpression("2");
 
-        SearchCond searchCondition = SearchCond.getOrCond(SearchCond.getLeafCond(usernameLeafCond),
+        SearchCond searchCondition = SearchCond.getAndCond(SearchCond.getLeafCond(usernameLeafCond),
                 SearchCond.getLeafCond(idRightCond));
 
         List<User> matchingUsers = searchDAO.search(RoleEntitlementUtil.getRoleKeys(entitlementDAO.findAll()),
@@ -276,13 +290,13 @@ public class AttributableSearchTest extends AbstractTest {
     }
 
     @Test
-    public void searchByRolenameAndId() {
+    public void searchByRolenameAndKey() {
         SubjectCond rolenameLeafCond = new SubjectCond(SubjectCond.Type.EQ);
         rolenameLeafCond.setSchema("name");
         rolenameLeafCond.setExpression("root");
 
         SubjectCond idRightCond = new SubjectCond(SubjectCond.Type.LT);
-        idRightCond.setSchema("id");
+        idRightCond.setSchema("key");
         idRightCond.setExpression("2");
 
         SearchCond searchCondition = SearchCond.getAndCond(SearchCond.getLeafCond(rolenameLeafCond),

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/entity/NotificationTest.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/entity/NotificationTest.java b/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/entity/NotificationTest.java
index 7cd9eae..91ad890 100644
--- a/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/entity/NotificationTest.java
+++ b/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/entity/NotificationTest.java
@@ -65,7 +65,7 @@ public class NotificationTest extends AbstractTest {
         notification.setRecipients("fake search condition");
 
         notification.setRecipientAttrName("email");
-        notification.setRecipientAttrType(IntMappingType.UserSchema);
+        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 
         notification.setSender("syncope@syncope.apache.org");
         notification.setSubject("Test notification");
@@ -92,7 +92,7 @@ public class NotificationTest extends AbstractTest {
         notification.setRecipients("fake search condition");
 
         notification.setRecipientAttrName("email");
-        notification.setRecipientAttrType(IntMappingType.UserSchema);
+        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 
         notification.addStaticRecipient("syncope445@syncope.apache.org");
 
@@ -115,7 +115,7 @@ public class NotificationTest extends AbstractTest {
         notification.setRoleAbout("fake search condition");
 
         notification.setRecipientAttrName("email");
-        notification.setRecipientAttrType(IntMappingType.UserSchema);
+        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 
         notification.addStaticRecipient("syncope446@syncope.apache.org");
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/entity/ResourceTest.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/entity/ResourceTest.java b/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/entity/ResourceTest.java
index c1c2355..68d1516 100644
--- a/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/entity/ResourceTest.java
+++ b/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/entity/ResourceTest.java
@@ -133,7 +133,7 @@ public class ResourceTest extends AbstractTest {
 
         UMappingItem accountId = entityFactory.newEntity(UMappingItem.class);
         accountId.setAccountid(true);
-        accountId.setIntMappingType(IntMappingType.UserSchema);
+        accountId.setIntMappingType(IntMappingType.UserPlainSchema);
         mapping.addItem(accountId);
 
         // save the resource
@@ -176,12 +176,12 @@ public class ResourceTest extends AbstractTest {
         UMappingItem item = entityFactory.newEntity(UMappingItem.class);
         item.setAccountid(true);
         item.setIntAttrName("fullname");
-        item.setIntMappingType(IntMappingType.UserSchema);
+        item.setIntMappingType(IntMappingType.UserPlainSchema);
         mapping.addItem(item);
 
         item = entityFactory.newEntity(UMappingItem.class);
         item.setIntAttrName("userId");
-        item.setIntMappingType(IntMappingType.UserSchema);
+        item.setIntMappingType(IntMappingType.UserPlainSchema);
         mapping.addItem(item);
 
         ExternalResource actual = resourceDAO.save(resource);
@@ -202,14 +202,14 @@ public class ResourceTest extends AbstractTest {
         UMappingItem item = entityFactory.newEntity(UMappingItem.class);
         item.setIntAttrName("fullname");
         item.setExtAttrName("fullname");
-        item.setIntMappingType(IntMappingType.UserSchema);
+        item.setIntMappingType(IntMappingType.UserPlainSchema);
         item.setPurpose(MappingPurpose.BOTH);
         mapping.setAccountIdItem(item);
 
         item = entityFactory.newEntity(UMappingItem.class);
         item.setIntAttrName("icon");
         item.setExtAttrName("icon");
-        item.setIntMappingType(IntMappingType.RoleSchema);
+        item.setIntMappingType(IntMappingType.RolePlainSchema);
         item.setPurpose(MappingPurpose.BOTH);
         mapping.addItem(item);
 


[09/15] syncope git commit: FIT server integration tests

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/UserSelfITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/UserSelfITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/UserSelfITCase.java
new file mode 100644
index 0000000..8795f06
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/UserSelfITCase.java
@@ -0,0 +1,342 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+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.io.IOException;
+import java.io.InputStream;
+import java.security.AccessControlException;
+import java.util.Map;
+import javax.ws.rs.core.Response;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.mod.AttrMod;
+import org.apache.syncope.common.lib.mod.MembershipMod;
+import org.apache.syncope.common.lib.mod.StatusMod;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.MembershipTO;
+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.SubjectType;
+import org.apache.syncope.common.rest.api.Preference;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.UserSelfService;
+import org.apache.syncope.common.rest.api.service.UserService;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class UserSelfITCase extends AbstractITCase {
+
+    @Test
+    public void selfRegistrationAllowed() {
+        assertTrue(clientFactory.createAnonymous().isSelfRegAllowed());
+    }
+
+    @Test
+    public void create() {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers());
+
+        // 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.createAnonymous();
+        UserTO self = anonClient.getService(UserSelfService.class).
+                create(UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org"), true).
+                readEntity(UserTO.class);
+        assertNotNull(self);
+        assertEquals("createApproval", self.getStatus());
+    }
+
+    @Test
+    public void createAndApprove() {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers());
+
+        // self-create user with membership: goes 'createApproval' with resources and membership but no propagation
+        UserTO userTO = UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org");
+        MembershipTO membership = new MembershipTO();
+        membership.setRoleId(3L);
+        userTO.getMemberships().add(membership);
+        userTO.getResources().add(RESOURCE_NAME_TESTDB);
+
+        SyncopeClient anonClient = clientFactory.createAnonymous();
+        userTO = anonClient.getService(UserSelfService.class).
+                create(userTO, true).
+                readEntity(UserTO.class);
+        assertNotNull(userTO);
+        assertEquals("createApproval", userTO.getStatus());
+        assertFalse(userTO.getMemberships().isEmpty());
+        assertFalse(userTO.getResources().isEmpty());
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_TESTDB, SubjectType.USER, 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.getConnectorObject(RESOURCE_NAME_TESTDB, SubjectType.USER, 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);
+        }
+
+        UserSelfService userSelfService2 = clientFactory.create("rossini", ADMIN_PWD).getService(UserSelfService.class);
+        UserTO userTO = userSelfService2.read();
+        assertEquals("rossini", userTO.getUsername());
+    }
+
+    @Test
+    public void updateWithoutApproval() {
+        // 1. create user as admin
+        UserTO created = createUser(UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org"));
+        assertNotNull(created);
+        assertFalse(created.getUsername().endsWith("XX"));
+
+        // 2. self-update (username) - works
+        UserMod userMod = new UserMod();
+        userMod.setUsername(created.getUsername() + "XX");
+
+        SyncopeClient authClient = clientFactory.create(created.getUsername(), "password123");
+        UserTO updated = authClient.getService(UserSelfService.class).update(created.getKey(), userMod).
+                readEntity(UserTO.class);
+        assertNotNull(updated);
+        assertEquals(ActivitiDetector.isActivitiEnabledForUsers() ? "active" : "created", updated.getStatus());
+        assertTrue(updated.getUsername().endsWith("XX"));
+    }
+
+    @Test
+    public void updateWitApproval() {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers());
+
+        // 1. create user as admin
+        UserTO created = createUser(UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org"));
+        assertNotNull(created);
+        assertFalse(created.getUsername().endsWith("XX"));
+
+        // 2. self-update (username + memberships + resource) - works but needs approval
+        MembershipMod membershipMod = new MembershipMod();
+        membershipMod.setRole(7L);
+        AttrMod testAttrMod = new AttrMod();
+        testAttrMod.setSchema("testAttribute");
+        testAttrMod.getValuesToBeAdded().add("a value");
+        membershipMod.getPlainAttrsToUpdate().add(testAttrMod);
+
+        UserMod userMod = new UserMod();
+        userMod.setUsername(created.getUsername() + "XX");
+        userMod.getMembershipsToAdd().add(membershipMod);
+        userMod.getResourcesToAdd().add(RESOURCE_NAME_TESTDB);
+        userMod.setPassword("newPassword123");
+        StatusMod statusMod = new StatusMod();
+        statusMod.setOnSyncope(false);
+        statusMod.getResourceNames().add(RESOURCE_NAME_TESTDB);
+        userMod.setPwdPropRequest(statusMod);
+
+        SyncopeClient authClient = clientFactory.create(created.getUsername(), "password123");
+        UserTO updated = authClient.getService(UserSelfService.class).update(created.getKey(), userMod).
+                readEntity(UserTO.class);
+        assertNotNull(updated);
+        assertEquals("updateApproval", updated.getStatus());
+        assertFalse(updated.getUsername().endsWith("XX"));
+        assertTrue(updated.getMemberships().isEmpty());
+
+        // no propagation happened
+        assertTrue(updated.getResources().isEmpty());
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_TESTDB, SubjectType.USER, 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.getConnectorObject(RESOURCE_NAME_TESTDB, SubjectType.USER, updated.getKey()));
+    }
+
+    @Test
+    public void delete() {
+        UserTO created = createUser(UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org"));
+        assertNotNull(created);
+
+        SyncopeClient authClient = clientFactory.create(created.getUsername(), "password123");
+        UserTO deleted = authClient.getService(UserSelfService.class).delete().readEntity(UserTO.class);
+        assertNotNull(deleted);
+        assertEquals(ActivitiDetector.isActivitiEnabledForUsers() ? "deleteApproval" : null, deleted.getStatus());
+    }
+
+    @Test
+    public void issueSYNCOPE373() {
+        UserTO userTO = userSelfService.read();
+        assertEquals(ADMIN_UNAME, userTO.getUsername());
+    }
+
+    @Test
+    public void noContent() throws IOException {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers());
+
+        SyncopeClient anonClient = clientFactory.createAnonymous();
+        UserSelfService noContentService = anonClient.prefer(UserSelfService.class, Preference.RETURN_NO_CONTENT);
+
+        UserTO user = UserITCase.getUniqueSampleTO("nocontent-anonymous@syncope.apache.org");
+
+        Response response = noContentService.create(user, true);
+        assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
+        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
+        assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
+    }
+
+    @Test
+    public void passwordReset() {
+        // 1. create an user with security question and answer
+        UserTO user = UserITCase.getUniqueSampleTO("pwdReset@syncope.apache.org");
+        user.setSecurityQuestion(1L);
+        user.setSecurityAnswer("Rossi");
+        createUser(user);
+
+        // 2. verify that new user is able to authenticate
+        SyncopeClient authClient = clientFactory.create(user.getUsername(), "password123");
+        UserTO read = authClient.getService(UserSelfService.class).read();
+        assertNotNull(read);
+
+        // 3. request password reset (as anonymous) providing the expected security answer
+        SyncopeClient anonClient = clientFactory.createAnonymous();
+        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, "newPassword");
+
+        // 6. verify that password was reset and token removed
+        authClient = clientFactory.create(user.getUsername(), "newPassword");
+        read = authClient.getService(UserSelfService.class).read();
+        assertNotNull(read);
+        assertNull(read.getToken());
+    }
+
+    @Test
+    public void passwordResetWithoutSecurityQuestion() {
+        // 0. disable security question for password reset
+        configurationService.set("passwordReset.securityQuestion",
+                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.getService(UserSelfService.class).read();
+        assertNotNull(read);
+
+        // 3. request password reset (as anonymous) with no security answer
+        SyncopeClient anonClient = clientFactory.createAnonymous();
+        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, "newPassword");
+
+        // 6. verify that password was reset and token removed
+        authClient = clientFactory.create(user.getUsername(), "newPassword");
+        read = authClient.getService(UserSelfService.class).read();
+        assertNotNull(read);
+        assertNull(read.getToken());
+
+        // 7. re-enable security question for password reset
+        configurationService.set("passwordReset.securityQuestion",
+                attrTO("passwordReset.securityQuestion", "true"));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/UserWorkflowITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/UserWorkflowITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/UserWorkflowITCase.java
new file mode 100644
index 0000000..9c81d0f
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/UserWorkflowITCase.java
@@ -0,0 +1,299 @@
+/*
+ * 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.server.reference;
+
+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.mod.UserMod;
+import org.apache.syncope.common.lib.to.MembershipTO;
+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.rest.api.service.UserWorkflowService;
+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());
+
+        UserTO userTO = UserITCase.getUniqueSampleTO("createWithReject@syncope.apache.org");
+        userTO.getResources().add(RESOURCE_NAME_TESTDB);
+
+        // User with role 9 are defined in workflow as subject to approval
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(9L);
+        userTO.getMemberships().add(membershipTO);
+
+        // 1. create user with role 9
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+        assertEquals(1, userTO.getMemberships().size());
+        assertEquals(9, userTO.getMemberships().get(0).getRoleId());
+        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());
+        assertNotNull(form.getTaskId());
+        assertNull(form.getOwner());
+
+        // 3. claim task from rossini, not in role 7 (designated for approval in workflow definition): fail
+        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, in role 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());
+
+        // 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 role 9 are defined in workflow as subject to approval
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(9L);
+        userTO.getMemberships().add(membershipTO);
+
+        // 1. create user with role 9 (and verify that no propagation occurred)
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+        assertEquals(1, userTO.getMemberships().size());
+        assertEquals(9, userTO.getMemberships().get(0).getRoleId());
+        assertEquals("createApproval", userTO.getStatus());
+        assertEquals(Collections.singleton(RESOURCE_NAME_TESTDB), userTO.getResources());
+
+        assertTrue(userTO.getPropagationStatusTOs().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
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.setPassword("anotherPassword123");
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+    }
+
+    @Test
+    public void issueSYNCOPE15() {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers());
+
+        // 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 role 9 are defined in workflow as subject to approval
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(9L);
+        userTO.getMemberships().add(membershipTO);
+
+        // 1. create user with role 9 (and verify that no propagation occurred)
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+        assertNotEquals(0L, userTO.getKey());
+        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
+        final 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/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/VirAttrITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/VirAttrITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/VirAttrITCase.java
new file mode 100644
index 0000000..1dc6f20
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/VirAttrITCase.java
@@ -0,0 +1,872 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Map;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.mod.AttrMod;
+import org.apache.syncope.common.lib.mod.MembershipMod;
+import org.apache.syncope.common.lib.mod.StatusMod;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.lib.to.ConnObjectTO;
+import org.apache.syncope.common.lib.to.MappingItemTO;
+import org.apache.syncope.common.lib.to.MappingTO;
+import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.ConnConfProperty;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.common.lib.types.SubjectType;
+import org.apache.syncope.common.rest.api.service.ResourceService;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class VirAttrITCase extends AbstractITCase {
+
+    @Test
+    public void issueSYNCOPE16() {
+        UserTO userTO = UserITCase.getUniqueSampleTO("issue16@apache.org");
+
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(8L);
+        userTO.getMemberships().add(membershipTO);
+
+        // 1. create user
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+
+        // 2. check for virtual attribute value
+        actual = userService.read(actual.getKey());
+        assertNotNull(actual);
+        assertEquals("virtualvalue", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+        UserMod userMod = new UserMod();
+        userMod.setKey(actual.getKey());
+        userMod.getVirAttrsToRemove().add("virtualdata");
+        userMod.getVirAttrsToUpdate().add(attrMod("virtualdata", "virtualupdated"));
+
+        // 3. update virtual attribute
+        actual = updateUser(userMod);
+        assertNotNull(actual);
+
+        // 4. check for virtual attribute value
+        actual = userService.read(actual.getKey());
+        assertNotNull(actual);
+        assertEquals("virtualupdated", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
+    }
+
+    @Test
+    public void issueSYNCOPE260() {
+        // ----------------------------------
+        // create user and check virtual attribute value propagation
+        // ----------------------------------
+        UserTO userTO = UserITCase.getUniqueSampleTO("260@a.com");
+        userTO.getResources().add(RESOURCE_NAME_WS2);
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+        assertFalse(userTO.getPropagationStatusTOs().isEmpty());
+        assertEquals(RESOURCE_NAME_WS2, userTO.getPropagationStatusTOs().get(0).getResource());
+        assertEquals(PropagationTaskExecStatus.SUBMITTED, userTO.getPropagationStatusTOs().get(0).getStatus());
+
+        ConnObjectTO connObjectTO =
+                resourceService.getConnectorObject(RESOURCE_NAME_WS2, SubjectType.USER, userTO.getKey());
+        assertNotNull(connObjectTO);
+        assertEquals("virtualvalue", connObjectTO.getPlainAttrMap().get("NAME").getValues().get(0));
+        // ----------------------------------
+
+        // ----------------------------------
+        // update user virtual attribute and check virtual attribute value update propagation
+        // ----------------------------------
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+
+        AttrMod attrMod = new AttrMod();
+        attrMod.setSchema("virtualdata");
+        attrMod.getValuesToBeRemoved().add("virtualvalue");
+        attrMod.getValuesToBeAdded().add("virtualvalue2");
+
+        userMod.getVirAttrsToUpdate().add(attrMod);
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+        assertFalse(userTO.getPropagationStatusTOs().isEmpty());
+        assertEquals("ws-target-resource-2", userTO.getPropagationStatusTOs().get(0).getResource());
+        assertEquals(PropagationTaskExecStatus.SUBMITTED, userTO.getPropagationStatusTOs().get(0).getStatus());
+
+        connObjectTO = resourceService.getConnectorObject(RESOURCE_NAME_WS2, SubjectType.USER, userTO.getKey());
+        assertNotNull(connObjectTO);
+        assertEquals("virtualvalue2", connObjectTO.getPlainAttrMap().get("NAME").getValues().get(0));
+        // ----------------------------------
+
+        // ----------------------------------
+        // suspend/reactivate user and check virtual attribute value (unchanged)
+        // ----------------------------------
+        StatusMod statusMod = new StatusMod();
+        statusMod.setType(StatusMod.ModType.SUSPEND);
+        userTO = userService.status(userTO.getKey(), statusMod).readEntity(UserTO.class);
+        assertEquals("suspended", userTO.getStatus());
+
+        connObjectTO = resourceService.getConnectorObject(RESOURCE_NAME_WS2, SubjectType.USER, userTO.getKey());
+        assertNotNull(connObjectTO);
+        assertFalse(connObjectTO.getPlainAttrMap().get("NAME").getValues().isEmpty());
+        assertEquals("virtualvalue2", connObjectTO.getPlainAttrMap().get("NAME").getValues().get(0));
+
+        statusMod = new StatusMod();
+        statusMod.setType(StatusMod.ModType.REACTIVATE);
+        userTO = userService.status(userTO.getKey(), statusMod).readEntity(UserTO.class);
+        assertEquals("active", userTO.getStatus());
+
+        connObjectTO = resourceService.getConnectorObject(RESOURCE_NAME_WS2, SubjectType.USER, userTO.getKey());
+        assertNotNull(connObjectTO);
+        assertFalse(connObjectTO.getPlainAttrMap().get("NAME").getValues().isEmpty());
+        assertEquals("virtualvalue2", connObjectTO.getPlainAttrMap().get("NAME").getValues().get(0));
+        // ----------------------------------
+
+        // ----------------------------------
+        // update user attribute and check virtual attribute value (unchanged)
+        // ----------------------------------
+        userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+
+        attrMod = new AttrMod();
+        attrMod.setSchema("surname");
+        attrMod.getValuesToBeRemoved().add("Surname");
+        attrMod.getValuesToBeAdded().add("Surname2");
+
+        userMod.getPlainAttrsToUpdate().add(attrMod);
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+        assertFalse(userTO.getPropagationStatusTOs().isEmpty());
+        assertEquals(RESOURCE_NAME_WS2, userTO.getPropagationStatusTOs().get(0).getResource());
+        assertEquals(PropagationTaskExecStatus.SUBMITTED, userTO.getPropagationStatusTOs().get(0).getStatus());
+
+        connObjectTO = resourceService.getConnectorObject(RESOURCE_NAME_WS2, SubjectType.USER, userTO.getKey());
+        assertNotNull(connObjectTO);
+        assertEquals("Surname2", connObjectTO.getPlainAttrMap().get("SURNAME").getValues().get(0));
+
+        // attribute "name" mapped on virtual attribute "virtualdata" shouldn't be changed
+        assertFalse(connObjectTO.getPlainAttrMap().get("NAME").getValues().isEmpty());
+        assertEquals("virtualvalue2", connObjectTO.getPlainAttrMap().get("NAME").getValues().get(0));
+        // ----------------------------------
+
+        // ----------------------------------
+        // remove user virtual attribute and check virtual attribute value (reset)
+        // ----------------------------------
+        userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.getVirAttrsToRemove().add("virtualdata");
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+        assertTrue(userTO.getVirAttrs().isEmpty());
+        assertFalse(userTO.getPropagationStatusTOs().isEmpty());
+        assertEquals(RESOURCE_NAME_WS2, userTO.getPropagationStatusTOs().get(0).getResource());
+        assertEquals(PropagationTaskExecStatus.SUBMITTED, userTO.getPropagationStatusTOs().get(0).getStatus());
+
+        connObjectTO = resourceService.getConnectorObject(RESOURCE_NAME_WS2, SubjectType.USER, userTO.getKey());
+        assertNotNull(connObjectTO);
+
+        // attribute "name" mapped on virtual attribute "virtualdata" should be reset
+        assertTrue(connObjectTO.getPlainAttrMap().get("NAME").getValues() == null
+                || connObjectTO.getPlainAttrMap().get("NAME").getValues().isEmpty());
+        // ----------------------------------
+    }
+
+    @Test
+    public void virAttrCache() {
+        UserTO userTO = UserITCase.getUniqueSampleTO("virattrcache@apache.org");
+        userTO.getVirAttrs().clear();
+
+        AttrTO virAttrTO = new AttrTO();
+        virAttrTO.setSchema("virtualdata");
+        virAttrTO.getValues().add("virattrcache");
+        userTO.getVirAttrs().add(virAttrTO);
+
+        userTO.getMemberships().clear();
+        userTO.getResources().clear();
+        userTO.getResources().add(RESOURCE_NAME_DBVIRATTR);
+
+        // 1. create user
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+
+        // 2. check for virtual attribute value
+        actual = userService.read(actual.getKey());
+        assertEquals("virattrcache", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+        // 3. update virtual attribute directly
+        final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+
+        String value = jdbcTemplate.queryForObject(
+                "SELECT USERNAME FROM testsync WHERE ID=?", String.class, actual.getKey());
+        assertEquals("virattrcache", value);
+
+        jdbcTemplate.update("UPDATE testsync set USERNAME='virattrcache2' WHERE ID=?", actual.getKey());
+
+        value = jdbcTemplate.queryForObject(
+                "SELECT USERNAME FROM testsync WHERE ID=?", String.class, actual.getKey());
+        assertEquals("virattrcache2", value);
+
+        // 4. check for cached attribute value
+        actual = userService.read(actual.getKey());
+        assertEquals("virattrcache", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+        UserMod userMod = new UserMod();
+        userMod.setKey(actual.getKey());
+
+        AttrMod virtualdata = new AttrMod();
+        virtualdata.setSchema("virtualdata");
+        virtualdata.getValuesToBeAdded().add("virtualupdated");
+
+        userMod.getVirAttrsToRemove().add("virtualdata");
+        userMod.getVirAttrsToUpdate().add(virtualdata);
+
+        // 5. update virtual attribute
+        actual = updateUser(userMod);
+        assertNotNull(actual);
+
+        // 6. check for virtual attribute value
+        actual = userService.read(actual.getKey());
+        assertNotNull(actual);
+        assertEquals("virtualupdated", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
+    }
+
+    @Test
+    public void issueSYNCOPE397() {
+        ResourceTO csv = resourceService.read(RESOURCE_NAME_CSV);
+        final MappingTO origMapping = SerializationUtils.clone(csv.getUmapping());
+        try {
+            // change mapping of resource-csv
+            assertNotNull(origMapping);
+            for (MappingItemTO item : csv.getUmapping().getItems()) {
+                if ("email".equals(item.getIntAttrName())) {
+                    // unset internal attribute mail and set virtual attribute virtualdata as mapped to external email
+                    item.setIntMappingType(IntMappingType.UserVirtualSchema);
+                    item.setIntAttrName("virtualdata");
+                    item.setPurpose(MappingPurpose.BOTH);
+                    item.setExtAttrName("email");
+                }
+            }
+
+            resourceService.update(csv.getKey(), csv);
+            csv = resourceService.read(RESOURCE_NAME_CSV);
+            assertNotNull(csv.getUmapping());
+
+            boolean found = false;
+            for (MappingItemTO item : csv.getUmapping().getItems()) {
+                if ("email".equals(item.getExtAttrName()) && "virtualdata".equals(item.getIntAttrName())) {
+                    found = true;
+                }
+            }
+
+            assertTrue(found);
+
+            // create a new user
+            UserTO userTO = UserITCase.getUniqueSampleTO("syncope397@syncope.apache.org");
+            userTO.getResources().clear();
+            userTO.getMemberships().clear();
+            userTO.getDerAttrs().clear();
+            userTO.getVirAttrs().clear();
+
+            userTO.getDerAttrs().add(attrTO("csvuserid", null));
+            userTO.getDerAttrs().add(attrTO("cn", null));
+            userTO.getVirAttrs().add(attrTO("virtualdata", "test@testone.org"));
+            // assign resource-csv to user
+            userTO.getResources().add(RESOURCE_NAME_CSV);
+            // save user
+            UserTO created = createUser(userTO);
+            // make std controls about user
+            assertNotNull(created);
+            assertTrue(RESOURCE_NAME_CSV.equals(created.getResources().iterator().next()));
+            // update user
+            UserTO toBeUpdated = userService.read(created.getKey());
+            UserMod userMod = new UserMod();
+            userMod.setKey(toBeUpdated.getKey());
+            userMod.setPassword("password2");
+            // assign new resource to user
+            userMod.getResourcesToAdd().add(RESOURCE_NAME_WS2);
+            //modify virtual attribute
+            userMod.getVirAttrsToRemove().add("virtualdata");
+            userMod.getVirAttrsToUpdate().add(attrMod("virtualdata", "test@testoneone.com"));
+
+            // check Syncope change password
+            StatusMod pwdPropRequest = new StatusMod();
+            pwdPropRequest.setOnSyncope(true);
+            pwdPropRequest.getResourceNames().add(RESOURCE_NAME_WS2);
+            userMod.setPwdPropRequest(pwdPropRequest);
+
+            toBeUpdated = updateUser(userMod);
+            assertNotNull(toBeUpdated);
+            assertEquals("test@testoneone.com", toBeUpdated.getVirAttrs().get(0).getValues().get(0));
+            // check if propagates correctly with assertEquals on size of tasks list
+            assertEquals(2, toBeUpdated.getPropagationStatusTOs().size());
+        } finally {
+            // restore mapping of resource-csv
+            csv.setUmapping(origMapping);
+            resourceService.update(csv.getKey(), csv);
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE442() {
+        UserTO userTO = UserITCase.getUniqueSampleTO("syncope442@apache.org");
+        userTO.getVirAttrs().clear();
+
+        AttrTO virAttrTO = new AttrTO();
+        virAttrTO.setSchema("virtualdata");
+        virAttrTO.getValues().add("virattrcache");
+        userTO.getVirAttrs().add(virAttrTO);
+
+        userTO.getMemberships().clear();
+        userTO.getResources().clear();
+        userTO.getResources().add(RESOURCE_NAME_DBVIRATTR);
+
+        // 1. create user
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+
+        // 2. check for virtual attribute value
+        actual = userService.read(actual.getKey());
+        assertEquals("virattrcache", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+        // ----------------------------------------
+        // 3. force cache expiring without any modification
+        // ----------------------------------------
+        String jdbcURL = null;
+        ConnInstanceTO connInstanceBean = connectorService.readByResource(RESOURCE_NAME_DBVIRATTR);
+        for (ConnConfProperty prop : connInstanceBean.getConfiguration()) {
+            if ("jdbcUrlTemplate".equals(prop.getSchema().getName())) {
+                jdbcURL = prop.getValues().iterator().next().toString();
+                prop.getValues().clear();
+                prop.getValues().add("jdbc:h2:tcp://localhost:9092/xxx");
+            }
+        }
+
+        connectorService.update(connInstanceBean.getKey(), connInstanceBean);
+
+        UserMod userMod = new UserMod();
+        userMod.setKey(actual.getKey());
+
+        AttrMod virtualdata = new AttrMod();
+        virtualdata.setSchema("virtualdata");
+        virtualdata.getValuesToBeAdded().add("virtualupdated");
+
+        userMod.getVirAttrsToRemove().add("virtualdata");
+        userMod.getVirAttrsToUpdate().add(virtualdata);
+
+        actual = updateUser(userMod);
+        assertNotNull(actual);
+        // ----------------------------------------
+
+        // ----------------------------------------
+        // 4. update virtual attribute
+        // ----------------------------------------
+        final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+
+        String value = jdbcTemplate.queryForObject(
+                "SELECT USERNAME FROM testsync WHERE ID=?", String.class, actual.getKey());
+        assertEquals("virattrcache", value);
+
+        jdbcTemplate.update("UPDATE testsync set USERNAME='virattrcache2' WHERE ID=?", actual.getKey());
+
+        value = jdbcTemplate.queryForObject(
+                "SELECT USERNAME FROM testsync WHERE ID=?", String.class, actual.getKey());
+        assertEquals("virattrcache2", value);
+        // ----------------------------------------
+
+        actual = userService.read(actual.getKey());
+        assertEquals("virattrcache", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+        // ----------------------------------------
+        // 5. restore connector
+        // ----------------------------------------
+        for (ConnConfProperty prop : connInstanceBean.getConfiguration()) {
+            if ("jdbcUrlTemplate".equals(prop.getSchema().getName())) {
+                prop.getValues().clear();
+                prop.getValues().add(jdbcURL);
+            }
+        }
+
+        connectorService.update(connInstanceBean.getKey(), connInstanceBean);
+        // ----------------------------------------
+
+        actual = userService.read(actual.getKey());
+        assertEquals("virattrcache2", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
+    }
+
+    @Test
+    public void issueSYNCOPE436() {
+        UserTO userTO = UserITCase.getUniqueSampleTO("syncope436@syncope.apache.org");
+        userTO.getMemberships().clear();
+        userTO.getResources().clear();
+        userTO.getResources().add(RESOURCE_NAME_LDAP);
+        userTO.getVirAttrs().add(attrTO("virtualReadOnly", "readOnly"));
+        userTO = createUser(userTO);
+        //Finding no values because the virtual attribute is readonly 
+        assertTrue(userTO.getVirAttrMap().get("virtualReadOnly").getValues().isEmpty());
+    }
+
+    @Test
+    public void issueSYNCOPE453() {
+        final String resourceName = "issueSYNCOPE453-Res-" + getUUIDString();
+        final String roleName = "issueSYNCOPE453-Role-" + getUUIDString();
+
+        // -------------------------------------------
+        // Create a resource ad-hoc
+        // -------------------------------------------
+        final ResourceTO resourceTO = new ResourceTO();
+
+        resourceTO.setKey(resourceName);
+        resourceTO.setConnectorId(107L);
+
+        MappingTO mapping = new MappingTO();
+
+        MappingItemTO item = new MappingItemTO();
+        item.setIntAttrName("aLong");
+        item.setIntMappingType(IntMappingType.UserPlainSchema);
+        item.setExtAttrName(roleName);
+        item.setPurpose(MappingPurpose.PROPAGATION);
+        item.setAccountid(true);
+        mapping.setAccountIdItem(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.RoleVirtualSchema);
+        item.setPurpose(MappingPurpose.PROPAGATION);
+        mapping.getItems().add(item);
+
+        resourceTO.setUmapping(mapping);
+        assertNotNull(getObject(
+                resourceService.create(resourceTO).getLocation(), ResourceService.class, ResourceTO.class));
+        // -------------------------------------------
+
+        // -------------------------------------------
+        // Create a role ad-hoc
+        // -------------------------------------------
+        RoleTO roleTO = new RoleTO();
+        roleTO.setName(roleName);
+        roleTO.setParent(8L);
+        roleTO.getRVirAttrTemplates().add("rvirtualdata");
+        roleTO.getVirAttrs().add(attrTO("rvirtualdata", "ml@role.it"));
+        roleTO.getResources().add(RESOURCE_NAME_LDAP);
+        roleTO = createRole(roleTO);
+        assertEquals(1, roleTO.getVirAttrs().size());
+        assertEquals("ml@role.it", roleTO.getVirAttrs().get(0).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();
+
+        final MembershipTO membership = new MembershipTO();
+        membership.setRoleId(roleTO.getKey());
+        membership.getVirAttrs().add(attrTO("mvirtualdata", "mvirtualvalue"));
+        userTO.getMemberships().add(membership);
+
+        userTO = createUser(userTO);
+        assertEquals(2, userTO.getPropagationStatusTOs().size());
+        assertTrue(userTO.getPropagationStatusTOs().get(0).getStatus().isSuccessful());
+        assertTrue(userTO.getPropagationStatusTOs().get(1).getStatus().isSuccessful());
+
+        JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+
+        final Map<String, Object> actuals = jdbcTemplate.queryForMap(
+                "SELECT id, surname, email FROM testsync WHERE id=?",
+                new Object[] { Integer.parseInt(userTO.getPlainAttrMap().get("aLong").getValues().get(0)) });
+
+        assertEquals(userTO.getPlainAttrMap().get("aLong").getValues().get(0), actuals.get("id").toString());
+        assertEquals("ml@role.it", actuals.get("email"));
+        // -------------------------------------------
+
+        // -------------------------------------------
+        // Delete resource and role ad-hoc
+        // -------------------------------------------
+        resourceService.delete(resourceName);
+        roleService.delete(roleTO.getKey());
+        // -------------------------------------------
+    }
+
+    @Test
+    public void issueSYNCOPE459() {
+        UserTO userTO = UserITCase.getUniqueSampleTO("syncope459@apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getVirAttrs().clear();
+
+        final AttrTO virtualReadOnly = attrTO("virtualReadOnly", "");
+        virtualReadOnly.getValues().clear();
+
+        userTO.getVirAttrs().add(virtualReadOnly);
+
+        userTO = createUser(userTO);
+
+        assertNotNull(userTO.getVirAttrMap().get("virtualReadOnly"));
+
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+
+        AttrMod virtualdata = new AttrMod();
+        virtualdata.setSchema("virtualdata");
+
+        userMod.getVirAttrsToUpdate().add(virtualdata);
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO.getVirAttrMap().get("virtualdata"));
+    }
+
+    @Test
+    public void issueSYNCOPE458() {
+        // -------------------------------------------
+        // Create a role ad-hoc
+        // -------------------------------------------
+        final String roleName = "issueSYNCOPE458-Role-" + getUUIDString();
+        RoleTO roleTO = new RoleTO();
+        roleTO.setName(roleName);
+        roleTO.setParent(2L);
+        roleTO.setInheritTemplates(true);
+        roleTO = createRole(roleTO);
+        // -------------------------------------------
+
+        // -------------------------------------------
+        // Update resource-db-virattr mapping adding new membership virtual schema mapping
+        // -------------------------------------------
+        ResourceTO resourceDBVirAttr = resourceService.read(RESOURCE_NAME_DBVIRATTR);
+        assertNotNull(resourceDBVirAttr);
+
+        final MappingTO resourceUMapping = resourceDBVirAttr.getUmapping();
+
+        MappingItemTO item = new MappingItemTO();
+        item.setIntAttrName("mvirtualdata");
+        item.setIntMappingType(IntMappingType.MembershipVirtualSchema);
+        item.setExtAttrName("EMAIL");
+        item.setPurpose(MappingPurpose.BOTH);
+
+        resourceUMapping.addItem(item);
+
+        resourceDBVirAttr.setUmapping(resourceUMapping);
+
+        resourceService.update(RESOURCE_NAME_DBVIRATTR, resourceDBVirAttr);
+        // -------------------------------------------
+
+        // -------------------------------------------
+        // Create new user
+        // -------------------------------------------
+        UserTO userTO = UserITCase.getUniqueSampleTO("syncope458@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getResources().add(RESOURCE_NAME_DBVIRATTR);
+        userTO.getVirAttrs().clear();
+        userTO.getDerAttrs().clear();
+        userTO.getMemberships().clear();
+
+        // add membership, with virtual attribute populated, to user
+        MembershipTO membership = new MembershipTO();
+        membership.setRoleId(roleTO.getKey());
+        membership.getVirAttrs().add(attrTO("mvirtualdata", "syncope458@syncope.apache.org"));
+        userTO.getMemberships().add(membership);
+
+        //propagate user
+        userTO = createUser(userTO);
+        assertEquals(1, userTO.getPropagationStatusTOs().size());
+        assertTrue(userTO.getPropagationStatusTOs().get(0).getStatus().isSuccessful());
+       // -------------------------------------------
+
+        // 1. check if membership has virtual attribute populated
+        assertNotNull(userTO.getMemberships().get(0).getVirAttrMap().get("mvirtualdata"));
+        assertEquals("syncope458@syncope.apache.org",
+                userTO.getMemberships().get(0).getVirAttrMap().get("mvirtualdata").getValues().get(0));
+        // -------------------------------------------
+
+        // 2. update membership virtual attribute
+        MembershipMod membershipMod = new MembershipMod();
+        membershipMod.setRole(roleTO.getKey());
+        membershipMod.getVirAttrsToUpdate().add(attrMod("mvirtualdata", "syncope458_NEW@syncope.apache.org"));
+
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.getMembershipsToAdd().add(membershipMod);
+        userMod.getMembershipsToRemove().add(userTO.getMemberships().iterator().next().getKey());
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+        // 3. check again after update if membership has virtual attribute populated with new value
+        assertNotNull(userTO.getMemberships().get(0).getVirAttrMap().get("mvirtualdata"));
+        assertEquals("syncope458_NEW@syncope.apache.org", userTO.getMemberships().get(0).getVirAttrMap().get(
+                "mvirtualdata").getValues().get(0));
+
+        // ----------------------------------------
+        // force cache expiring without any modification
+        // ----------------------------------------
+        String jdbcURL = null;
+        ConnInstanceTO connInstanceBean = connectorService.readByResource(RESOURCE_NAME_DBVIRATTR);
+        for (ConnConfProperty prop : connInstanceBean.getConfiguration()) {
+            if ("jdbcUrlTemplate".equals(prop.getSchema().getName())) {
+                jdbcURL = prop.getValues().iterator().next().toString();
+                prop.getValues().clear();
+                prop.getValues().add("jdbc:h2:tcp://localhost:9092/xxx");
+            }
+        }
+
+        connectorService.update(connInstanceBean.getKey(), connInstanceBean);
+
+        membershipMod = new MembershipMod();
+        membershipMod.setRole(roleTO.getKey());
+        membershipMod.getVirAttrsToUpdate().add(attrMod("mvirtualdata", "syncope458_updated@syncope.apache.org"));
+
+        userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.getMembershipsToAdd().add(membershipMod);
+        userMod.getMembershipsToRemove().add(userTO.getMemberships().iterator().next().getKey());
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+        // ----------------------------------
+
+        // change attribute value directly on resource
+        final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+
+        String value = jdbcTemplate.queryForObject(
+                "SELECT EMAIL FROM testsync WHERE ID=?", String.class, userTO.getKey());
+        assertEquals("syncope458_NEW@syncope.apache.org", value);
+
+        jdbcTemplate.update("UPDATE testsync set EMAIL='syncope458_NEW_TWO@syncope.apache.org' WHERE ID=?", userTO.
+                getKey());
+
+        value = jdbcTemplate.queryForObject("SELECT EMAIL FROM testsync WHERE ID=?", String.class, userTO.getKey());
+        assertEquals("syncope458_NEW_TWO@syncope.apache.org", value);
+        // ----------------------------------------
+
+        // ----------------------------------------
+        // restore connector
+        // ----------------------------------------
+        for (ConnConfProperty prop : connInstanceBean.getConfiguration()) {
+            if ("jdbcUrlTemplate".equals(prop.getSchema().getName())) {
+                prop.getValues().clear();
+                prop.getValues().add(jdbcURL);
+            }
+        }
+        connectorService.update(connInstanceBean.getKey(), connInstanceBean);
+        // ----------------------------------------
+
+        userTO = userService.read(userTO.getKey());
+        assertNotNull(userTO);
+        // 4. check virtual attribute synchronization after direct update on resource
+        assertEquals("syncope458_NEW_TWO@syncope.apache.org", userTO.getMemberships().get(0).getVirAttrMap().get(
+                "mvirtualdata").getValues().get(0));
+
+        // 5. remove membership virtual attribute
+        membershipMod = new MembershipMod();
+        membershipMod.setRole(roleTO.getKey());
+        membershipMod.getVirAttrsToRemove().add("mvirtualdata");
+
+        userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.getMembershipsToAdd().add(membershipMod);
+        userMod.getMembershipsToRemove().add(userTO.getMemberships().iterator().next().getKey());
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+        // check again after update if membership hasn't any virtual attribute
+        assertTrue(userTO.getMemberships().get(0).getVirAttrMap().isEmpty());
+
+        // -------------------------------------------
+        // Delete role ad-hoc and restore resource mapping
+        // -------------------------------------------
+        roleService.delete(roleTO.getKey());
+
+        resourceUMapping.removeItem(item);
+        resourceDBVirAttr.setUmapping(resourceUMapping);
+        resourceService.update(RESOURCE_NAME_DBVIRATTR, resourceDBVirAttr);
+        // -------------------------------------------
+    }
+
+    @Test
+    public void issueSYNCOPE501() {
+        // PHASE 1: update only user virtual attributes
+
+        // 1. create user and propagate him on resource-db-virattr
+        UserTO userTO = UserITCase.getUniqueSampleTO("syncope501@apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getVirAttrs().clear();
+
+        userTO.getResources().add(RESOURCE_NAME_DBVIRATTR);
+
+        // virtualdata is mapped with username
+        final AttrTO virtualData = attrTO("virtualdata", "syncope501@apache.org");
+        userTO.getVirAttrs().add(virtualData);
+
+        userTO = createUser(userTO);
+
+        assertNotNull(userTO.getVirAttrMap().get("virtualdata"));
+        assertEquals("syncope501@apache.org", userTO.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+        // 2. update virtual attribute
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+
+        final StatusMod statusMod = new StatusMod();
+        statusMod.getResourceNames().addAll(Collections.<String>emptySet());
+        statusMod.setOnSyncope(false);
+
+        userMod.setPwdPropRequest(statusMod);
+        // change virtual attribute value
+        final AttrMod virtualDataMod = new AttrMod();
+        virtualDataMod.setSchema("virtualdata");
+        virtualDataMod.getValuesToBeAdded().add("syncope501_updated@apache.org");
+        virtualDataMod.getValuesToBeRemoved().add("syncope501@apache.org");
+        userMod.getVirAttrsToUpdate().add(virtualDataMod);
+        userMod.getVirAttrsToRemove().add("virtualdata");
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+
+        // 3. check that user virtual attribute has really been updated 
+        assertFalse(userTO.getVirAttrMap().get("virtualdata").getValues().isEmpty());
+        assertEquals("syncope501_updated@apache.org", userTO.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+        // ----------------------------------------------------------
+        // PHASE 2: update only membership virtual attributes
+        // -------------------------------------------
+        // Update resource-db-virattr mapping adding new membership virtual schema mapping
+        // -------------------------------------------
+        ResourceTO resourceDBVirAttr = resourceService.read(RESOURCE_NAME_DBVIRATTR);
+        assertNotNull(resourceDBVirAttr);
+
+        final MappingTO resourceUMapping = resourceDBVirAttr.getUmapping();
+
+        MappingItemTO item = new MappingItemTO();
+        item.setIntAttrName("mvirtualdata");
+        item.setIntMappingType(IntMappingType.MembershipVirtualSchema);
+        item.setExtAttrName("EMAIL");
+        item.setPurpose(MappingPurpose.BOTH);
+
+        resourceUMapping.addItem(item);
+
+        resourceDBVirAttr.setUmapping(resourceUMapping);
+
+        resourceService.update(RESOURCE_NAME_DBVIRATTR, resourceDBVirAttr);
+        // -------------------------------------------
+
+        // -------------------------------------------
+        // Create a role ad-hoc
+        // -------------------------------------------
+        final String roleName = "issueSYNCOPE501-Role-" + getUUIDString();
+        RoleTO roleTO = new RoleTO();
+        roleTO.setName(roleName);
+        roleTO.setParent(2L);
+        roleTO.setInheritTemplates(true);
+        roleTO = createRole(roleTO);
+        // -------------------------------------------
+
+        // 1. add membership, with virtual attribute populated, to user
+        MembershipMod membershipMod = new MembershipMod();
+        membershipMod.setRole(roleTO.getKey());
+        membershipMod.getVirAttrsToUpdate().add(attrMod("mvirtualdata", "syncope501membership@test.org"));
+
+        userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.getMembershipsToAdd().add(membershipMod);
+        userMod.setPwdPropRequest(statusMod);
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+        assertEquals("syncope501membership@test.org",
+                userTO.getMemberships().get(0).getVirAttrMap().get("mvirtualdata").getValues().get(0));
+
+        // 2. update only membership virtual attribute and propagate user
+        membershipMod = new MembershipMod();
+        membershipMod.setRole(roleTO.getKey());
+        membershipMod.getVirAttrsToUpdate().add(attrMod("mvirtualdata",
+                "syncope501membership_updated@test.org"));
+        membershipMod.getVirAttrsToRemove().add("syncope501membership@test.org");
+
+        userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.getMembershipsToAdd().add(membershipMod);
+        userMod.getMembershipsToRemove().add(userTO.getMemberships().iterator().next().getKey());
+        userMod.setPwdPropRequest(statusMod);
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+
+        // 3. check if change has been propagated
+        assertEquals("syncope501membership_updated@test.org", userTO.getMemberships().get(0).getVirAttrMap().
+                get("mvirtualdata").getValues().get(0));
+
+        // 4. delete membership and check on resource attribute deletion
+        userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.getMembershipsToRemove().add(userTO.getMemberships().get(0).getKey());
+        userMod.setPwdPropRequest(statusMod);
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+        assertTrue(userTO.getMemberships().isEmpty());
+
+        // read attribute value directly on resource
+        final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+
+        final String emailValue = jdbcTemplate.queryForObject(
+                "SELECT EMAIL FROM testsync WHERE ID=?", String.class, userTO.getKey());
+        assertTrue(StringUtils.isBlank(emailValue));
+        // ----------------------------------------
+
+        // -------------------------------------------
+        // Delete role ad-hoc and restore resource mapping
+        // -------------------------------------------
+        roleService.delete(roleTO.getKey());
+
+        resourceUMapping.removeItem(item);
+        resourceDBVirAttr.setUmapping(resourceUMapping);
+        resourceService.update(RESOURCE_NAME_DBVIRATTR, resourceDBVirAttr);
+        // -------------------------------------------
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/VirSchemaITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/VirSchemaITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/VirSchemaITCase.java
new file mode 100644
index 0000000..884b5cb
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/VirSchemaITCase.java
@@ -0,0 +1,122 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.VirSchemaTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+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.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(AttributableType.USER, SchemaType.VIRTUAL);
+        assertFalse(vSchemas.isEmpty());
+        for (VirSchemaTO vSchemaTO : vSchemas) {
+            assertNotNull(vSchemaTO);
+        }
+    }
+
+    @Test
+    public void read() {
+        VirSchemaTO vSchemaTO = schemaService.read(AttributableType.MEMBERSHIP, SchemaType.VIRTUAL,
+                "mvirtualdata");
+        assertNotNull(vSchemaTO);
+    }
+
+    @Test
+    public void create() {
+        VirSchemaTO schema = new VirSchemaTO();
+        schema.setKey("virtual");
+
+        VirSchemaTO actual = createSchema(AttributableType.USER, SchemaType.VIRTUAL, schema);
+        assertNotNull(actual);
+
+        actual = schemaService.read(AttributableType.USER, SchemaType.VIRTUAL, actual.getKey());
+        assertNotNull(actual);
+    }
+
+    @Test
+    public void delete() {
+        VirSchemaTO schema = schemaService.read(AttributableType.ROLE, SchemaType.VIRTUAL, "rvirtualdata");
+        assertNotNull(schema);
+
+        schemaService.delete(AttributableType.ROLE, SchemaType.VIRTUAL, schema.getKey());
+
+        try {
+            schemaService.read(AttributableType.ROLE, SchemaType.VIRTUAL, "rvirtualdata");
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE323() {
+        VirSchemaTO actual = schemaService.read(AttributableType.MEMBERSHIP, SchemaType.VIRTUAL, "mvirtualdata");
+        assertNotNull(actual);
+
+        try {
+            createSchema(AttributableType.MEMBERSHIP, SchemaType.VIRTUAL, actual);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.CONFLICT, e.getType().getResponseStatus());
+            assertEquals(ClientExceptionType.EntityExists, e.getType());
+        }
+
+        actual.setKey(null);
+        try {
+            createSchema(AttributableType.MEMBERSHIP, SchemaType.VIRTUAL, actual);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.BAD_REQUEST, e.getType().getResponseStatus());
+            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE418() {
+        VirSchemaTO schema = new VirSchemaTO();
+        schema.setKey("http://schemas.examples.org/security/authorization/organizationUnit");
+
+        try {
+            createSchema(AttributableType.MEMBERSHIP, SchemaType.VIRTUAL, schema);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidVirSchema, e.getType());
+
+            assertTrue(e.getElements().iterator().next().toString().contains(EntityViolationType.InvalidName.name()));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/WorkflowITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/WorkflowITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/WorkflowITCase.java
new file mode 100644
index 0000000..161a23c
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/WorkflowITCase.java
@@ -0,0 +1,86 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+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.SubjectType;
+import org.junit.Assume;
+import org.junit.Test;
+
+public class WorkflowITCase extends AbstractITCase {
+
+    @Test
+    public void isActivitiEnabled() {
+        assertEquals(ActivitiDetector.isActivitiEnabledForUsers(),
+                adminClient.isActivitiEnabledFor(SubjectType.USER));
+        assertEquals(ActivitiDetector.isActivitiEnabledForRoles(),
+                adminClient.isActivitiEnabledFor(SubjectType.ROLE));
+    }
+
+    private void exportDefinition(final SubjectType 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());
+        exportDefinition(SubjectType.USER);
+    }
+
+    @Test
+    public void getRoleDefinition() throws IOException {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForRoles());
+        exportDefinition(SubjectType.ROLE);
+    }
+
+    private void importDefinition(final SubjectType 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());
+
+        importDefinition(SubjectType.USER);
+    }
+
+    @Test
+    public void updateRoleDefinition() throws IOException {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForRoles());
+
+        importDefinition(SubjectType.ROLE);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/resources/favicon.jpg
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/resources/favicon.jpg b/syncope620/fit/reference/src/test/resources/favicon.jpg
new file mode 100644
index 0000000..cf212be
Binary files /dev/null and b/syncope620/fit/reference/src/test/resources/favicon.jpg differ

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/resources/test.csv
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/resources/test.csv b/syncope620/fit/reference/src/test/resources/test.csv
new file mode 100644
index 0000000..f227366
--- /dev/null
+++ b/syncope620/fit/reference/src/test/resources/test.csv
@@ -0,0 +1,10 @@
+test0,nome0,cognome0,test0@syncope.apache.org,password0,role1,membership1,true,false
+test1,nome1,cognome1,test1@syncope.apache.org,password1,role1,membership1,false,false
+test2,nome2,cognome2,test2@syncope.apache.org,notpermitted1,role1,membership1,true,false
+test3,nome3,cognome3,test3@syncope.apache.org,password3,role1,membership1,true,false
+test4,nome4,cognome4,test4@syncope.apache.org,password4,role1,membership1,true,false
+test5,nome5,cognome5,test5@syncope.apache.org,password5,role1,membership1,true,false
+test6,nome6,cognome6,test6@syncope.apache.org,password6,role1,membership1,true,false
+test7,nome7,cognome7,test7@syncope.apache.org,password7,role1,membership1,true,false
+test8,nome8,cognome8,test8@syncope.apache.org,password8,role1,membership1,true,false
+test9,nome9,cognome9,test9@syncope.apache.org,password999,role1,membership1,true,false

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/resources/testJDBCContext.xml
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/resources/testJDBCContext.xml b/syncope620/fit/reference/src/test/resources/testJDBCContext.xml
new file mode 100644
index 0000000..d89e067
--- /dev/null
+++ b/syncope620/fit/reference/src/test/resources/testJDBCContext.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                           http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+  <bean id="testDataSource"
+        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
+    <property name="driverClassName" value="${testdb.driver}"/>
+    <property name="url" value="${testdb.url}"/>
+    <property name="username" value="${testdb.username}"/>
+    <property name="password" value="${testdb.password}"/>
+  </bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/pom.xml
----------------------------------------------------------------------
diff --git a/syncope620/pom.xml b/syncope620/pom.xml
index 4b0e328..b784741 100644
--- a/syncope620/pom.xml
+++ b/syncope620/pom.xml
@@ -315,8 +315,8 @@ under the License.
 
     <connid.version>1.4.0.0</connid.version>
     <connid.soap.version>1.3.0</connid.soap.version>
-    <connid.db.table.version>2.2.0</connid.db.table.version>
-    <connid.csvdir.version>0.8</connid.csvdir.version>
+    <connid.db.table.version>2.2.1</connid.db.table.version>
+    <connid.csvdir.version>0.8.1</connid.csvdir.version>
     <connid.ldap.version>1.4.0</connid.ldap.version>
     <connid.ad.version>1.2.1</connid.ad.version>
 
@@ -382,7 +382,7 @@ under the License.
     <cargo.log>${log.directory}/cargo.log</cargo.log>
     <cargo.output>${log.directory}/cargo-output.log</cargo.output>
 
-    <tomcat.version>8.0.15</tomcat.version>
+    <tomcat.version>8.0.17</tomcat.version>
 
     <anonymousUser>anonymous</anonymousUser>
     <!-- static keys, only used for build: generated overlays will override during archetype:generate -->
@@ -881,6 +881,12 @@ under the License.
         </plugin>
         
         <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-javadoc-plugin</artifactId>
+          <version>2.10.1</version>          
+        </plugin>
+        
+        <plugin>
           <groupId>org.apache.openjpa</groupId>
           <artifactId>openjpa-maven-plugin</artifactId>
           <version>${openjpa.version}</version>
@@ -929,9 +935,15 @@ under the License.
         </plugin>
         
         <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-antrun-plugin</artifactId>
+          <version>1.8</version>
+        </plugin>
+        
+        <plugin>
           <groupId>org.codehaus.cargo</groupId>
           <artifactId>cargo-maven2-plugin</artifactId>
-          <version>1.4.11</version>
+          <version>1.4.12</version>
           <configuration>
             <container>
               <containerId>tomcat8x</containerId>
@@ -950,7 +962,7 @@ under the License.
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-surefire-plugin</artifactId>
-          <version>2.18</version>
+          <version>2.18.1</version>
           <configuration>
             <redirectTestOutputToFile>true</redirectTestOutputToFile>
             <encoding>utf-8</encoding>
@@ -958,6 +970,24 @@ under the License.
             <argLine>-Xms512m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512m</argLine>
           </configuration>
         </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-failsafe-plugin</artifactId>
+          <version>2.18.1</version>
+          <configuration>
+            <redirectTestOutputToFile>true</redirectTestOutputToFile>
+            <encoding>utf-8</encoding>
+            <runOrder>alphabetical</runOrder>
+          </configuration>
+          <executions>
+            <execution>
+              <goals>
+                <goal>integration-test</goal>
+                <goal>verify</goal>
+              </goals>
+            </execution>
+          </executions>
+        </plugin>
         
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
@@ -986,6 +1016,110 @@ under the License.
     </plugins>
   </build>
 
+  <profiles>
+
+    <profile>
+      <id>apache-release</id>
+
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-deploy-plugin</artifactId>
+            <version>2.8.2</version>
+          </plugin>  
+
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-gpg-plugin</artifactId>
+            <version>1.6</version>
+            <executions>
+              <execution>
+                <id>sign-artifacts</id>
+                <phase>verify</phase>
+                <goals>
+                  <goal>sign</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+
+    <profile>
+      <id>site</id>
+      <build>
+        <defaultGoal>clean site-deploy</defaultGoal>
+
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-site-plugin</artifactId>
+            <version>3.4</version>
+            <configuration>
+              <locales>en</locales>
+              <generateProjectInfo>false</generateProjectInfo>
+              <reportPlugins>
+                <plugin>
+                  <groupId>org.apache.maven.plugins</groupId>
+                  <artifactId>maven-project-info-reports-plugin</artifactId>
+                  <version>2.8</version>
+                  <configuration>
+                    <dependencyDetailsEnabled>false</dependencyDetailsEnabled>
+                    <dependencyLocationsEnabled>false</dependencyLocationsEnabled>
+                  </configuration>
+                  <reports>
+                    <report>index</report>
+                    <report>mailing-list</report>
+                    <report>project-team</report>
+                    <report>issue-tracking</report>
+                    <report>license</report>
+                  </reports>
+                </plugin>
+                <plugin>
+                  <groupId>org.apache.maven.plugins</groupId>
+                  <artifactId>maven-javadoc-plugin</artifactId>
+                  <version>2.10.1</version>
+                  <configuration>
+                    <destDir>apidocs/2.0</destDir>
+                    <detectLinks>true</detectLinks>
+                    <detectJavaApiLink>true</detectJavaApiLink>
+                    <links>
+                      <link>http://docs.oracle.com/javaee/7/api/</link>
+                      <link>http://www.slf4j.org/api/</link>
+                      <link>http://connid.tirasa.net/apidocs/1.4/</link>
+                      <link>http://camel.apache.org/maven/current/camel-core/apidocs/</link>
+                      <link>http://camel.apache.org/maven/current/camel-spring/apidocs/</link>
+                      <link>http://ci.apache.org/projects/wicket/apidocs/7.x/</link>
+                      <link>http://docs.spring.io/spring/docs/4.1.x/javadoc-api/</link>
+                      <link>http://docs.spring.io/spring-security/site/docs/3.2.x/apidocs/</link>
+                      <link>http://activiti.org/javadocs/</link>
+                    </links>
+                  </configuration>
+                  <reports>
+                    <report>aggregate</report>
+                  </reports>
+                </plugin>
+              </reportPlugins>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+
+    <profile>
+      <id>skipTests</id>
+      <properties>
+        <skipTests>true</skipTests>
+      </properties>
+      <build>
+        <defaultGoal>clean install</defaultGoal>
+      </build>
+    </profile>
+
+  </profiles>
+  
   <modules>
     <module>common</module>
     <module>server</module>


[02/15] syncope git commit: Merge branch '1_2_X'

Posted by il...@apache.org.
Merge branch '1_2_X'


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/336d8d6f
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/336d8d6f
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/336d8d6f

Branch: refs/heads/2_0_X
Commit: 336d8d6f29b96e25ea2994868d945c89c3ac3e91
Parents: 8d270a1 4efca7c
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Wed Jan 21 06:54:51 2015 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Wed Jan 21 06:54:51 2015 +0100

----------------------------------------------------------------------
 .../core/rest/ConfigurationTestITCase.java      | 79 +++++++++++---------
 1 file changed, 42 insertions(+), 37 deletions(-)
----------------------------------------------------------------------



[07/15] syncope git commit: FIT server integration tests

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/relationship/ResourceTest.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/relationship/ResourceTest.java b/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/relationship/ResourceTest.java
index c5fbf29..dc9124b 100644
--- a/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/relationship/ResourceTest.java
+++ b/syncope620/server/persistence-jpa/src/test/java/org/apache/syncope/server/persistence/jpa/relationship/ResourceTest.java
@@ -118,7 +118,7 @@ public class ResourceTest extends AbstractTest {
             UMappingItem item = entityFactory.newEntity(UMappingItem.class);
             item.setExtAttrName("test" + i);
             item.setIntAttrName("nonexistent" + i);
-            item.setIntMappingType(IntMappingType.UserSchema);
+            item.setIntMappingType(IntMappingType.UserPlainSchema);
             item.setMandatoryCondition("false");
             item.setPurpose(MappingPurpose.SYNCHRONIZATION);
             mapping.addItem(item);
@@ -257,13 +257,16 @@ public class ResourceTest extends AbstractTest {
         List<? extends RMappingItem> items = ldap.getRmapping().getItems();
         assertNotNull(items);
         assertFalse(items.isEmpty());
-        List<Long> itemIds = new ArrayList<Long>(items.size());
+        List<Long> itemIds = new ArrayList<>(items.size());
         for (RMappingItem item : items) {
             itemIds.add(item.getKey());
         }
 
         ldap.setRmapping(null);
 
+        // need to avoid any class not defined in this Maven module
+        ldap.getPropagationActionsClassNames().clear();
+        
         resourceDAO.save(ldap);
         resourceDAO.flush();
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/persistence-jpa/src/test/resources/content.xml
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/test/resources/content.xml b/syncope620/server/persistence-jpa/src/test/resources/content.xml
index 0fb3b79..ecbf06d 100644
--- a/syncope620/server/persistence-jpa/src/test/resources/content.xml
+++ b/syncope620/server/persistence-jpa/src/test/resources/content.xml
@@ -407,10 +407,10 @@ under the License.
   <RDerAttrTemplate id="1003" owner_id="1" schema_name="rderToBePropagated"/>    
   <RDerAttr id="1003" owner_id="1" template_id="1003"/>    
 
-  <RVirAttrTemplate id="100" owner_id="4" schema_name="rvirtualdata"/>
-  <RVirAttr id="100" owner_id="4" template_id="100"/>
+  <RVirAttrTemplate id="98" owner_id="4" schema_name="rvirtualdata"/>
+  <RVirAttr id="98" owner_id="4" template_id="98"/>
 
-  <RVirAttrTemplate id="101" owner_id="3" schema_name="rvirtualdata"/>
+  <RVirAttrTemplate id="99" owner_id="3" schema_name="rvirtualdata"/>
 
   <MPlainAttrTemplate id="98" owner_id="1" schema_name="mderived_sx"/>
   
@@ -438,7 +438,7 @@ under the License.
   
   <MDerAttrTemplate id="100" owner_id="1" schema_name="mderToBePropagated"/>  
     
-  <MVirAttrTemplate id="100" owner_id="2" schema_name="mvirtualdata"/>
+  <MVirAttrTemplate id="99" owner_id="2" schema_name="mvirtualdata"/>
 
   <ConnInstance id="100" displayName="ConnInstance100"
                 location="${connid.location}"
@@ -605,7 +605,7 @@ under the License.
                     creator="admin" lastModifier="admin" 
                     creationDate="2010-10-20 11:00:00" lastChangeDate="2010-10-20 11:00:00"/>
   <ExternalResource_PropActions externalResource_name="resource-ldap"
-                                action="org.apache.syncope.server.provisioning.api.propagation.PropagationActions"/>
+                                action="org.apache.syncope.server.provisioning.java.propagation.LDAPMembershipPropagationActions"/>
   <ExternalResource name="ws-target-resource-nopropagation" connector_id="103"
                     randomPwdIfNotProvided="0" enforceMandatoryCondition="1" propagationMode="TWO_PHASES"
                     propagationPriority="0" propagationPrimary="0" createTraceLevel="ALL" deleteTraceLevel="ALL" updateTraceLevel="ALL" syncTraceLevel="ALL" 
@@ -664,49 +664,49 @@ under the License.
                 intMappingType="UserId" mandatoryCondition="true"
                 accountid="1" password="0" purpose="PROPAGATION"/>
   <UMappingItem id="100" extAttrName="email" mapping_id="15"
-                intAttrName="email" intMappingType="UserSchema" mandatoryCondition="true"
+                intAttrName="email" intMappingType="UserPlainSchema" mandatoryCondition="true"
                 accountid="0" password="0" purpose="PROPAGATION"/>
   <UMappingItem id="101" extAttrName="surname" mapping_id="15"
-                intAttrName="surname" intMappingType="UserSchema" mandatoryCondition="true"
+                intAttrName="surname" intMappingType="UserPlainSchema" mandatoryCondition="true"
                 accountid="0" password="0" purpose="PROPAGATION"/>
   <UMappingItem id="102" mapping_id="15"
                 extAttrName="__PASSWORD__" intMappingType="Password" mandatoryCondition="true"
                 accountid="0" password="1" purpose="PROPAGATION"/>
   <UMappingItem id="335" mapping_id="15" 
-                extAttrName="fullname" intAttrName="surname" intMappingType="UserSchema" mandatoryCondition="true"
+                extAttrName="fullname" intAttrName="surname" intMappingType="UserPlainSchema" mandatoryCondition="true"
                 accountid="0" password="0" purpose="PROPAGATION"/>
   <UMappingItem id="336" mapping_id="15"
-                extAttrName="type" intAttrName="type" intMappingType="UserSchema" mandatoryCondition="true"
+                extAttrName="type" intAttrName="type" intMappingType="UserPlainSchema" mandatoryCondition="true"
                 accountid="0" password="0" purpose="PROPAGATION"/>
   <UMappingItem id="337" mapping_id="15"
-                extAttrName="name" intAttrName="firstname" intMappingType="UserSchema" mandatoryCondition="false"
+                extAttrName="name" intAttrName="firstname" intMappingType="UserPlainSchema" mandatoryCondition="false"
                 accountid="0" password="0" purpose="NONE"/>
   
   <UMapping id="12" resource_name="ws-target-resource-list-mappings-1"/>
   <UMappingItem id="103" mapping_id="12"
-                extAttrName="email" intAttrName="email" intMappingType="UserSchema" mandatoryCondition="true"
+                extAttrName="email" intAttrName="email" intMappingType="UserPlainSchema" mandatoryCondition="true"
                 accountid="1" password="0" purpose="PROPAGATION"/>
   <UMappingItem id="104" extAttrName="surname" mapping_id="12"
-                intAttrName="surname" intMappingType="UserSchema" mandatoryCondition="true"
+                intAttrName="surname" intMappingType="UserPlainSchema" mandatoryCondition="true"
                 accountid="0" password="0" purpose="PROPAGATION"/>
 
   <UMapping id="13" resource_name="ws-target-resource-list-mappings-2"/>
   <UMappingItem id="105" mapping_id="13"
-                extAttrName="userId" intAttrName="userId" intMappingType="UserSchema" mandatoryCondition="true"
+                extAttrName="userId" intAttrName="userId" intMappingType="UserPlainSchema" mandatoryCondition="true"
                 accountid="1" password="0" purpose="PROPAGATION"/>
 
   <UMapping id="1" resource_name="ws-target-resource-2"/>
   <UMappingItem id="106" mapping_id="1" extAttrName="fullname"
-                intAttrName="fullname" intMappingType="UserSchema" mandatoryCondition="true"
+                intAttrName="fullname" intMappingType="UserPlainSchema" mandatoryCondition="true"
                 accountid="1" password="0" purpose="BOTH"/>
   <UMappingItem id="107" mapping_id="1"
                 extAttrName="__PASSWORD__" intMappingType="Password" mandatoryCondition="true"
                 accountid="0" password="1" purpose="BOTH"/>
   <UMappingItem id="108" extAttrName="type" mapping_id="1"
-                intAttrName="type" intMappingType="UserSchema" mandatoryCondition="true"
+                intAttrName="type" intMappingType="UserPlainSchema" mandatoryCondition="true"
                 accountid="0" password="0" purpose="BOTH"/>
   <UMappingItem id="109" extAttrName="surname" mapping_id="1"
-                intAttrName="surname" intMappingType="UserSchema" mandatoryCondition="type == 'F'"
+                intAttrName="surname" intMappingType="UserPlainSchema" mandatoryCondition="type == 'F'"
                 accountid="0" password="0" purpose="BOTH"/>
   <UMappingItem id="110" extAttrName="name" mapping_id="1"
                 intAttrName="virtualdata" intMappingType="UserVirtualSchema" mandatoryCondition="type == 'F'"
@@ -717,33 +717,33 @@ under the License.
     
   <UMapping id="2" resource_name="ws-target-resource-update"/>
   <UMappingItem id="112" extAttrName="email" mapping_id="2"
-                intAttrName="email" intMappingType="UserSchema" mandatoryCondition="false"
+                intAttrName="email" intMappingType="UserPlainSchema" mandatoryCondition="false"
                 accountid="0" password="0" purpose="PROPAGATION"/>
   <UMappingItem id="113" extAttrName="userId" mapping_id="2"
-                intAttrName="userId" intMappingType="UserSchema" mandatoryCondition="false"
+                intAttrName="userId" intMappingType="UserPlainSchema" mandatoryCondition="false"
                 accountid="1" password="0" purpose="PROPAGATION"/>
   <UMappingItem id="114" extAttrName="test3" mapping_id="2" 
-                intAttrName="fullname" intMappingType="UserSchema" mandatoryCondition="false"
+                intAttrName="fullname" intMappingType="UserPlainSchema" mandatoryCondition="false"
                 accountid="0" password="0" purpose="PROPAGATION"/>
     
   <UMapping id="3" resource_name="ws-target-resource-nopropagation"/>
   <UMappingItem id="115" mapping_id="3" extAttrName="fullname" 
-                intAttrName="fullname" intMappingType="UserSchema" mandatoryCondition="true"
+                intAttrName="fullname" intMappingType="UserPlainSchema" mandatoryCondition="true"
                 accountid="1" password="0" purpose="PROPAGATION"/>
                      
   <UMapping id="4" resource_name="ws-target-resource-nopropagation2"/>
   <UMappingItem id="116" mapping_id="4" extAttrName="fullname" 
-                intAttrName="fullname" intMappingType="UserSchema" mandatoryCondition="true"
+                intAttrName="fullname" intMappingType="UserPlainSchema" mandatoryCondition="true"
                 accountid="1" password="0" purpose="PROPAGATION"/>
                      
   <UMapping id="5" resource_name="ws-target-resource-nopropagation3"/>
   <UMappingItem id="117" mapping_id="5" extAttrName="fullname"
-                intAttrName="fullname" intMappingType="UserSchema" mandatoryCondition="true"
+                intAttrName="fullname" intMappingType="UserPlainSchema" mandatoryCondition="true"
                 accountid="1" password="0" purpose="PROPAGATION"/>
                      
   <UMapping id="6" resource_name="ws-target-resource-nopropagation4"/>
   <UMappingItem id="118" mapping_id="6"
-                extAttrName="fullname" intAttrName="fullname" intMappingType="UserSchema" mandatoryCondition="true"
+                extAttrName="fullname" intAttrName="fullname" intMappingType="UserPlainSchema" mandatoryCondition="true"
                 accountid="1" password="0" purpose="PROPAGATION"/>
                        
   <UMapping id="7" resource_name="resource-testdb"/>
@@ -767,22 +767,22 @@ under the License.
                 intMappingType="Username" mandatoryCondition="true"
                 accountid="0" password="0" purpose="BOTH"/>
   <UMappingItem id="201" extAttrName="id" mapping_id="9"
-                intAttrName="fullname" intMappingType="UserSchema" mandatoryCondition="true"
+                intAttrName="fullname" intMappingType="UserPlainSchema" mandatoryCondition="true"
                 accountid="0" password="0" purpose="BOTH"/>
   <UMappingItem id="202" mapping_id="9"
                 extAttrName="__PASSWORD__" intMappingType="Password" mandatoryCondition="true"
                 accountid="0" password="1" purpose="BOTH"/>
   <UMappingItem id="203" extAttrName="name" mapping_id="9"
-                intAttrName="firstname" intMappingType="UserSchema" mandatoryCondition="false"
+                intAttrName="firstname" intMappingType="UserPlainSchema" mandatoryCondition="false"
                 accountid="0" password="0" purpose="BOTH"/>
   <UMappingItem id="204" extAttrName="surname" mapping_id="9"
-                intAttrName="surname" intMappingType="UserSchema" mandatoryCondition="false"
+                intAttrName="surname" intMappingType="UserPlainSchema" mandatoryCondition="false"
                 accountid="0" password="0" purpose="BOTH"/>
   <UMappingItem id="205" extAttrName="email" mapping_id="9"
-                intAttrName="userId" intMappingType="UserSchema" mandatoryCondition="true"
+                intAttrName="userId" intMappingType="UserPlainSchema" mandatoryCondition="true"
                 accountid="0" password="0" purpose="SYNCHRONIZATION"/>
   <UMappingItem id="206" extAttrName="email" mapping_id="9"
-                intAttrName="email" intMappingType="UserSchema" mandatoryCondition="true"
+                intAttrName="email" intMappingType="UserPlainSchema" mandatoryCondition="true"
                 accountid="0" password="0" purpose="SYNCHRONIZATION"/>
   <UMappingItem id="207" extAttrName="__NAME__" mapping_id="9"
                 intAttrName="csvuserid" intMappingType="UserDerivedSchema" mandatoryCondition="true"
@@ -796,10 +796,10 @@ under the License.
                          
   <UMapping id="10" resource_name="ws-target-resource-update-resetsynctoken"/>
   <UMappingItem id="300" mapping_id="10"
-                extAttrName="userId" intAttrName="userId" intMappingType="UserSchema" mandatoryCondition="false"
+                extAttrName="userId" intAttrName="userId" intMappingType="UserPlainSchema" mandatoryCondition="false"
                 accountid="1" password="0" purpose="BOTH"/>
   <UMappingItem id="301" mapping_id="10"
-                extAttrName="__PASSWORD__" intAttrName="fullname" intMappingType="UserSchema" mandatoryCondition="false"
+                extAttrName="__PASSWORD__" intAttrName="fullname" intMappingType="UserPlainSchema" mandatoryCondition="false"
                 accountid="0" password="1" purpose="BOTH"/>
 
   <UMapping id="11" resource_name="resource-ldap"
@@ -811,31 +811,31 @@ under the License.
                 extAttrName="__PASSWORD__" intAttrName="Password" intMappingType="Password"
                 mandatoryCondition="true" purpose="BOTH"/>
   <UMappingItem id="313" accountid="0" password="0" mapping_id="11"
-                extAttrName="sn" intAttrName="surname" intMappingType="UserSchema"
+                extAttrName="sn" intAttrName="surname" intMappingType="UserPlainSchema"
                 mandatoryCondition="true" purpose="BOTH"/>
   <UMappingItem id="314" accountid="0" password="0" mapping_id="11"
-                extAttrName="cn" intAttrName="fullname" intMappingType="UserSchema"
+                extAttrName="cn" intAttrName="fullname" intMappingType="UserPlainSchema"
                 mandatoryCondition="true" purpose="BOTH"/>
   <UMappingItem id="315" accountid="0" password="0" mapping_id="11"
-                extAttrName="mail" intAttrName="email" intMappingType="UserSchema"
+                extAttrName="mail" intAttrName="email" intMappingType="UserPlainSchema"
                 mandatoryCondition="false" purpose="BOTH"/>
   <UMappingItem id="316" accountid="0" password="0" mapping_id="11"
-                extAttrName="title" intAttrName="title" intMappingType="RoleSchema"
+                extAttrName="title" intAttrName="title" intMappingType="RolePlainSchema"
                 mandatoryCondition="false" purpose="BOTH"/>
   <UMappingItem id="317" accountid="0" password="0" mapping_id="11"
-                extAttrName="postalAddress" intAttrName="postalAddress" intMappingType="MembershipSchema"
+                extAttrName="postalAddress" intAttrName="postalAddress" intMappingType="MembershipPlainSchema"
                 mandatoryCondition="false" purpose="BOTH"/>
   <UMappingItem id="318" accountid="0" password="0" mapping_id="11"
-                extAttrName="mail" intAttrName="userId" intMappingType="UserSchema"
+                extAttrName="mail" intAttrName="userId" intMappingType="UserPlainSchema"
                 mandatoryCondition="false" purpose="BOTH"/>
   <UMappingItem id="319" accountid="0" password="0" mapping_id="11"
                 extAttrName="givenname" intAttrName="virtualReadOnly" intMappingType="UserVirtualSchema"
                 mandatoryCondition="false" purpose="BOTH"/>
   <UMappingItem id="320" accountid="0" password="0" mapping_id="11"
-                extAttrName="registeredAddress" intAttrName="obscure" intMappingType="UserSchema"
+                extAttrName="registeredAddress" intAttrName="obscure" intMappingType="UserPlainSchema"
                 mandatoryCondition="false" purpose="BOTH"/>
   <UMappingItem id="321" accountid="0" password="0" mapping_id="11"
-                extAttrName="jpegPhoto" intAttrName="photo" intMappingType="UserSchema"
+                extAttrName="jpegPhoto" intAttrName="photo" intMappingType="UserPlainSchema"
                 mandatoryCondition="false" purpose="BOTH"/>
   
   <RMapping id="1" resource_name="resource-ldap"
@@ -847,7 +847,7 @@ under the License.
                 extAttrName="owner" intAttrName="roleOwnerSchema" intMappingType="RoleOwnerSchema"
                 mandatoryCondition="false" purpose="BOTH"/>
   <RMappingItem id="3" accountid="0" password="0" mapping_id="1"
-                extAttrName="description" intAttrName="title" intMappingType="RoleSchema"
+                extAttrName="description" intAttrName="title" intMappingType="RolePlainSchema"
                 mandatoryCondition="false" purpose="BOTH"/>
   <RMappingItem id="4" extAttrName="businessCategory" mapping_id="1"
                 intAttrName="rvirtualdata" intMappingType="RoleVirtualSchema" mandatoryCondition="false"
@@ -855,22 +855,22 @@ under the License.
         
   <UMapping id="16" resource_name="resource-db-sync"/>
   <UMappingItem id="322" accountid="0" mapping_id="16"
-                extAttrName="EMAIL" intAttrName="email" intMappingType="UserSchema" 
+                extAttrName="EMAIL" intAttrName="email" intMappingType="UserPlainSchema" 
                 mandatoryCondition="false" password="0" purpose="BOTH"/>
   <UMappingItem id="323" accountid="0" mapping_id="16"
-                extAttrName="SURNAME" intAttrName="fullname" intMappingType="UserSchema" 
+                extAttrName="SURNAME" intAttrName="fullname" intMappingType="UserPlainSchema" 
                 mandatoryCondition="false" password="0" purpose="BOTH"/>
   <UMappingItem id="324" accountid="1" mapping_id="16"
-                extAttrName="ID" intAttrName="aLong" intMappingType="UserSchema" 
+                extAttrName="ID" intAttrName="aLong" intMappingType="UserPlainSchema" 
                 mandatoryCondition="false" password="0" purpose="BOTH"/>
   <UMappingItem id="325" accountid="0" mapping_id="16"
-                extAttrName="SURNAME" intAttrName="surname" intMappingType="UserSchema" 
+                extAttrName="SURNAME" intAttrName="surname" intMappingType="UserPlainSchema" 
                 mandatoryCondition="false" password="0" purpose="BOTH"/>
   <UMappingItem id="326" accountid="0" mapping_id="16"
                 extAttrName="USERNAME" intAttrName="Username" intMappingType="Username" 
                 mandatoryCondition="false" password="0" purpose="BOTH"/>
   <UMappingItem id="327" accountid="0" mapping_id="16"
-                extAttrName="EMAIL" intAttrName="userId" intMappingType="UserSchema" 
+                extAttrName="EMAIL" intAttrName="userId" intMappingType="UserPlainSchema" 
                 mandatoryCondition="false" password="0" purpose="BOTH"/>
               
   <UMapping id="17" resource_name="resource-db-virattr"/>
@@ -883,7 +883,7 @@ under the License.
                 
   <UMapping id="18" resource_name="ws-target-resource-timeout"/>
   <UMappingItem id="333" mapping_id="18" accountid="1" password="0"
-                extAttrName="fullname" intAttrName="fullname" intMappingType="UserSchema"
+                extAttrName="fullname" intAttrName="fullname" intMappingType="UserPlainSchema"
                 mandatoryCondition="true" purpose="PROPAGATION"/>
   
   <UMapping id="19" resource_name="ws-target-resource-delete"/>
@@ -904,7 +904,7 @@ under the License.
   <Task DTYPE="SyncTask" type="SYNCHRONIZATION" id="4" name="CSV (update matching; assign unmatching)" resource_name="resource-csv"
         performCreate="1" performUpdate="1" performDelete="1" syncStatus="1" fullReconciliation="0"
         jobClassName="org.apache.syncope.server.provisioning.api.job.SyncJob" unmatchingRule="ASSIGN" matchingRule="UPDATE"
-        userTemplate='{"creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":0,"password":null,"status":null,"token":null,"tokenExpireTime":null,"username":null,"lastLoginDate":null,"changePwdDate":null,"failedLogins":null,"attributes":[{"schema":"type","readonly":false,"values":["email == &apos;test8@syncope.apache.org&apos;? &apos;TYPE_8&apos;: &apos;TYPE_OTHER&apos;"]}],"derivedAttributes":[{"schema":"cn","readonly":false,"values":[null]}],"virtualAttributes":[],"resources":["resource-testdb"],"propagationStatuses":[],"memberships":[{"creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"id":0,"roleId":8,"roleName":null,"attributes":[{"schema":"subscriptionDate","readonly":false,"values":["&apos;2009-08-18T16:33:12.203+0200&apos;"]}],"derivedAttributes":[],"virtualAttributes":[]}]}'
+        userTemplate='{"creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":0,"password":null,"status":null,"token":null,"tokenExpireTime":null,"username":null,"lastLoginDate":null,"changePwdDate":null,"failedLogins":null,"attributes":[{"schema":"type","readonly":false,"values":["email == &apos;test8@syncope.apache.org&apos;? &apos;TYPE_8&apos;: &apos;TYPE_OTHER&apos;"]}],"derivedAttributes":[{"schema":"cn","readonly":false,"values":[null]}],"virtualAttributes":[],"resources":["resource-testdb"],"propagationStatuses":[],"memberships":[{"creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":0,"roleId":8,"roleName":null,"attributes":[{"schema":"subscriptionDate","readonly":false,"values":["&apos;2009-08-18T16:33:12.203+0200&apos;"]}],"derivedAttributes":[],"virtualAttributes":[]}]}'
         roleTemplate='{"creator":null,"creationDate":null,"lastModifier":null,"lastChangeDate":null,"key":0,"name":null,"parent":0,"userOwner":null,"roleOwner":null,"inheritOwner":false,"inheritTemplates":false,"inheritPlainAttrs":false,"inheritDerAttrs":false,"inheritVirAttrs":false,"inheritPasswordPolicy":false,"inheritAccountPolicy":false,"passwordPolicy":null,"accountPolicy":null,"attributes":[],"derivedAttributes":[],"virtualAttributes":[],"resources":[],"propagationStatuses":[],"entitlements":[],"rAttrTemplates":[],"rDerAttrTemplates":[],"rVirAttrTemplates":[],"mAttrTemplates":[],"mDerAttrTemplates":[],"mVirAttrTemplates":[]}'/>
   <Task DTYPE="SchedTask" type="SCHEDULED" id="5" name="SampleJob Task" jobClassName="org.apache.syncope.server.provisioning.java.job.SampleJob" cronExpression="0 0 0 1 * ?"/>
   <Task DTYPE="PropagationTask" type="PROPAGATION" id="6" propagationMode="TWO_PHASES" propagationOperation="UPDATE"
@@ -989,12 +989,12 @@ under the License.
         performCreate="1" performUpdate="1" performDelete="1" syncStatus="1" fullReconciliation="0"
         jobClassName="org.apache.syncope.server.provisioning.api.job.SyncJob" unmatchingRule="ASSIGN" matchingRule="IGNORE"/>
 
-  <Notification id="1" active="1" recipientAttrName="email" recipientAttrType="UserSchema" selfAsRecipient="1" 
+  <Notification id="1" active="1" recipientAttrName="email" recipientAttrType="UserPlainSchema" selfAsRecipient="1" 
                 sender="admin@syncope.apache.org" subject="Password Reset request" template="requestPasswordReset" 
                 traceLevel="FAILURES" userAbout="token!=$null"/> 
   <Notification_events Notification_id="1" events="[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"/>
   
-  <Notification id="2" active="1" recipientAttrName="email" recipientAttrType="UserSchema" selfAsRecipient="1" 
+  <Notification id="2" active="1" recipientAttrName="email" recipientAttrType="UserPlainSchema" selfAsRecipient="1" 
                 sender="admin@syncope.apache.org" subject="Password Reset successful" template="confirmPasswordReset" 
                 traceLevel="FAILURES" userAbout="token!=$null"/> 
   <Notification_events Notification_id="2" events="[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"/>
@@ -1003,7 +1003,7 @@ under the License.
                 traceLevel="FAILURES"
                 userAbout="fullname==*o*;fullname==*i*"
                 recipients="$roles==7"
-                recipientAttrType="UserSchema" recipientAttrName="email" active="1"/>
+                recipientAttrType="UserPlainSchema" recipientAttrName="email" active="1"/>
   <Notification_events Notification_id="10" events="[CUSTOM]:[]:[]:[unexisting1]:[FAILURE]"/>
   <Notification_events Notification_id="10" events="[CUSTOM]:[]:[]:[unexisting2]:[SUCCESS]"/>
     
@@ -1011,7 +1011,7 @@ under the License.
   
   <Report id="1" name="test"/>
   <ReportletConfInstance id="1" Report_id="1" 
-                         serializedInstance='{"@class":"org.apache.syncope.common.lib.report.UserReportletConf","name":"testUserReportlet","matchingCond":null,"attributes":["fullname","gender"],"derivedAttributes":["cn"],"virtualAttributes":["virtualdata"],"features":["key","username","workflowId","status","creationDate","lastLoginDate","changePwdDate","passwordHistorySize","failedLoginCount","memberships","resources"]}'/>
+                         serializedInstance='{"@class":"org.apache.syncope.common.lib.report.UserReportletConf","name":"testUserReportlet","matchingCond":null,"plainAttributes":["fullname","gender"],"derivedAttributes":["cn"],"virtualAttributes":["virtualdata"],"features":["key","username","workflowId","status","creationDate","lastLoginDate","changePwdDate","passwordHistorySize","failedLoginCount","memberships","resources"]}'/>
   <ReportExec Report_id="1" id="1" status="SUCCESS" startDate="2012-02-26 15:40:04" endDate="2012-02-26 15:41:04"/>
   
   <SyncopeLogger logName="syncope.audit.[REST]:[EntitlementController]:[]:[getOwn]:[SUCCESS]" logLevel="DEBUG" logType="AUDIT"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/UserSuspender.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/UserSuspender.java b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/UserSuspender.java
new file mode 100644
index 0000000..218f826
--- /dev/null
+++ b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/UserSuspender.java
@@ -0,0 +1,26 @@
+/*
+ * 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.server.provisioning.api;
+
+import org.apache.syncope.server.persistence.api.entity.user.User;
+
+public interface UserSuspender {
+
+    void suspend(User user, boolean propagateSuspension);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/job/JobInstanceLoader.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/job/JobInstanceLoader.java b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/job/JobInstanceLoader.java
new file mode 100644
index 0000000..e77aaca
--- /dev/null
+++ b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/job/JobInstanceLoader.java
@@ -0,0 +1,41 @@
+/*
+ * 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.server.provisioning.api.job;
+
+import java.text.ParseException;
+import org.apache.syncope.server.persistence.api.entity.Report;
+import org.apache.syncope.server.persistence.api.entity.task.Task;
+import org.quartz.SchedulerException;
+
+public interface JobInstanceLoader {
+
+    void registerJob(Task task, String jobClassName, String cronExpression)
+            throws ClassNotFoundException, SchedulerException, ParseException;
+
+    void registerJob(Report report) throws SchedulerException, ParseException;
+
+    void registerReportJob(Long reportKey) throws SchedulerException, ParseException;
+
+    void registerTaskJob(Long taskKey) throws ClassNotFoundException, SchedulerException, ParseException;
+
+    void unregisterJob(Task task);
+
+    void unregisterJob(Report report);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/ProvisioningActions.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/ProvisioningActions.java b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/ProvisioningActions.java
index f7b1663..7277708 100644
--- a/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/ProvisioningActions.java
+++ b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/ProvisioningActions.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.server.provisioning.api.sync;
 
-import java.util.List;
 import org.quartz.JobExecutionException;
 
 public interface ProvisioningActions {
@@ -35,9 +34,7 @@ public interface ProvisioningActions {
      * Action to be executed after the synchronization task completion.
      *
      * @param profile sync profile
-     * @param results synchronization result
      * @throws JobExecutionException in case of generic failure
      */
-    void afterAll(final ProvisioningProfile<?, ?> profile, final List<ProvisioningResult> results)
-            throws JobExecutionException;
+    void afterAll(final ProvisioningProfile<?, ?> profile) throws JobExecutionException;
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/RolePushResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/RolePushResultHandler.java b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/RolePushResultHandler.java
new file mode 100644
index 0000000..0c765b5
--- /dev/null
+++ b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/RolePushResultHandler.java
@@ -0,0 +1,23 @@
+/*
+ * 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.server.provisioning.api.sync;
+
+public interface RolePushResultHandler extends SyncopePushResultHandler {
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/RoleSyncResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/RoleSyncResultHandler.java b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/RoleSyncResultHandler.java
new file mode 100644
index 0000000..1f77591
--- /dev/null
+++ b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/RoleSyncResultHandler.java
@@ -0,0 +1,26 @@
+/*
+ * 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.server.provisioning.api.sync;
+
+import java.util.Map;
+
+public interface RoleSyncResultHandler extends SyncopeSyncResultHandler {
+
+    Map<Long, String> getRoleOwnerMap();
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncopePushResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncopePushResultHandler.java b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncopePushResultHandler.java
new file mode 100644
index 0000000..bf6f680
--- /dev/null
+++ b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncopePushResultHandler.java
@@ -0,0 +1,26 @@
+/*
+ * 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.server.provisioning.api.sync;
+
+import org.apache.syncope.server.persistence.api.entity.task.PushTask;
+
+public interface SyncopePushResultHandler extends SyncopeResultHandler<PushTask, PushActions> {
+
+    boolean handle(long subjectId);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncopeSyncResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncopeSyncResultHandler.java b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncopeSyncResultHandler.java
new file mode 100644
index 0000000..111af19
--- /dev/null
+++ b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncopeSyncResultHandler.java
@@ -0,0 +1,29 @@
+/*
+ * 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.server.provisioning.api.sync;
+
+import org.apache.syncope.server.persistence.api.entity.task.SyncTask;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+import org.identityconnectors.framework.common.objects.SyncResultsHandler;
+
+public interface SyncopeSyncResultHandler extends SyncopeResultHandler<SyncTask, SyncActions>, SyncResultsHandler {
+
+    @Override
+    boolean handle(SyncDelta delta);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/UserPushResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/UserPushResultHandler.java b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/UserPushResultHandler.java
new file mode 100644
index 0000000..6d93a87
--- /dev/null
+++ b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/UserPushResultHandler.java
@@ -0,0 +1,23 @@
+/*
+ * 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.server.provisioning.api.sync;
+
+public interface UserPushResultHandler extends SyncopePushResultHandler {
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/UserSyncResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/UserSyncResultHandler.java b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/UserSyncResultHandler.java
new file mode 100644
index 0000000..00b972c
--- /dev/null
+++ b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/UserSyncResultHandler.java
@@ -0,0 +1,23 @@
+/*
+ * 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.server.provisioning.api.sync;
+
+public interface UserSyncResultHandler extends SyncopeSyncResultHandler {
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/AsyncConnectorFacade.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/AsyncConnectorFacade.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/AsyncConnectorFacade.java
index a22145a..b7e2197 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/AsyncConnectorFacade.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/AsyncConnectorFacade.java
@@ -58,7 +58,7 @@ public class AsyncConnectorFacade {
             final GuardedString password,
             final OperationOptions options) {
 
-        return new AsyncResult<Uid>(connector.authenticate(ObjectClass.ACCOUNT, username, password, options));
+        return new AsyncResult<>(connector.authenticate(ObjectClass.ACCOUNT, username, password, options));
     }
 
     @Async
@@ -68,7 +68,7 @@ public class AsyncConnectorFacade {
             final Set<Attribute> attrs,
             final OperationOptions options) {
 
-        return new AsyncResult<Uid>(connector.create(objectClass, attrs, options));
+        return new AsyncResult<>(connector.create(objectClass, attrs, options));
     }
 
     @Async
@@ -79,7 +79,7 @@ public class AsyncConnectorFacade {
             final Set<Attribute> attrs,
             final OperationOptions options) {
 
-        return new AsyncResult<Uid>(connector.update(objectClass, uid, attrs, options));
+        return new AsyncResult<>(connector.update(objectClass, uid, attrs, options));
     }
 
     @Async
@@ -90,14 +90,14 @@ public class AsyncConnectorFacade {
             final OperationOptions options) {
 
         connector.delete(objectClass, uid, options);
-        return new AsyncResult<Uid>(uid);
+        return new AsyncResult<>(uid);
     }
 
     @Async
     public Future<SyncToken> getLatestSyncToken(
             final ConnectorFacade connector, final ObjectClass objectClass) {
 
-        return new AsyncResult<SyncToken>(connector.getLatestSyncToken(objectClass));
+        return new AsyncResult<>(connector.getLatestSyncToken(objectClass));
     }
 
     @Async
@@ -107,7 +107,7 @@ public class AsyncConnectorFacade {
             final Uid uid,
             final OperationOptions options) {
 
-        return new AsyncResult<ConnectorObject>(connector.getObject(objectClass, uid, options));
+        return new AsyncResult<>(connector.getObject(objectClass, uid, options));
     }
 
     @Async
@@ -127,7 +127,7 @@ public class AsyncConnectorFacade {
             attribute = object.getAttributeByName(attributeName);
         }
 
-        return new AsyncResult<Attribute>(attribute);
+        return new AsyncResult<>(attribute);
     }
 
     @Async
@@ -137,7 +137,7 @@ public class AsyncConnectorFacade {
             final Uid uid,
             final OperationOptions options) {
 
-        final Set<Attribute> attributes = new HashSet<Attribute>();
+        final Set<Attribute> attributes = new HashSet<>();
 
         final ConnectorObject object = connector.getObject(objectClass, uid, options);
 
@@ -149,7 +149,7 @@ public class AsyncConnectorFacade {
             }
         }
 
-        return new AsyncResult<Set<Attribute>>(attributes);
+        return new AsyncResult<>(attributes);
     }
 
     @Async
@@ -170,7 +170,7 @@ public class AsyncConnectorFacade {
             LOG.debug("While reading schema on connector {}", connector, e);
         }
 
-        return new AsyncResult<Set<String>>(schemaNames);
+        return new AsyncResult<>(schemaNames);
     }
 
     @Async
@@ -187,18 +187,18 @@ public class AsyncConnectorFacade {
             LOG.debug("While reading schema on connector {}", connector, e);
         }
 
-        return new AsyncResult<Set<ObjectClass>>(objectClasses);
+        return new AsyncResult<>(objectClasses);
     }
 
     @Async
     public Future<String> validate(final ConnectorFacade connector) {
         connector.validate();
-        return new AsyncResult<String>("OK");
+        return new AsyncResult<>("OK");
     }
 
     @Async
     public Future<String> test(final ConnectorFacade connector) {
         connector.test();
-        return new AsyncResult<String>("OK");
+        return new AsyncResult<>("OK");
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/ConnectorFacadeProxy.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/ConnectorFacadeProxy.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/ConnectorFacadeProxy.java
index 3e5f03a..6557979 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/ConnectorFacadeProxy.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/ConnectorFacadeProxy.java
@@ -388,13 +388,12 @@ public class ConnectorFacadeProxy implements Connector {
 
             @Override
             public boolean handle(final ConnectorObject obj) {
-                final SyncDeltaBuilder bld = new SyncDeltaBuilder();
-                bld.setObject(obj);
-                bld.setUid(obj.getUid());
-                bld.setDeltaType(SyncDeltaType.CREATE_OR_UPDATE);
-                bld.setToken(new SyncToken(""));
-
-                return handler.handle(bld.build());
+                return handler.handle(new SyncDeltaBuilder().
+                        setObject(obj).
+                        setUid(obj.getUid()).
+                        setDeltaType(SyncDeltaType.CREATE_OR_UPDATE).
+                        setToken(new SyncToken("")).
+                        build());
             }
         }, options);
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/DefaultRoleProvisioningManager.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/DefaultRoleProvisioningManager.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/DefaultRoleProvisioningManager.java
index 666d535..06f205b 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/DefaultRoleProvisioningManager.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/DefaultRoleProvisioningManager.java
@@ -99,7 +99,7 @@ public class DefaultRoleProvisioningManager implements RoleProvisioningManager {
             final RoleTO roleTO, final Map<Long, String> roleOwnerMap, final Set<String> excludedResources) {
 
         WorkflowResult<Long> created = rwfAdapter.create((RoleTO) roleTO);
-        AttrTO roleOwner = roleTO.getAttrMap().get(StringUtils.EMPTY);
+        AttrTO roleOwner = roleTO.getPlainAttrMap().get(StringUtils.EMPTY);
         if (roleOwner != null) {
             roleOwnerMap.put(created.getResult(), roleOwner.getValues().iterator().next());
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/DefaultUserProvisioningManager.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/DefaultUserProvisioningManager.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/DefaultUserProvisioningManager.java
index 2c00a02..3c4225e 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/DefaultUserProvisioningManager.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/DefaultUserProvisioningManager.java
@@ -73,6 +73,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         return create(userTO, true, false, null, Collections.<String>emptySet());
     }
 
+    @Override
     public Map.Entry<Long, List<PropagationStatus>> create(final UserTO userTO, final boolean storePassword) {
         return create(userTO, storePassword, false, null, Collections.<String>emptySet());
     }
@@ -99,7 +100,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
             propagationReporter.onPrimaryResourceFailure(tasks);
         }
 
-        Map.Entry<Long, List<PropagationStatus>> result = new AbstractMap.SimpleEntry<Long, List<PropagationStatus>>(
+        Map.Entry<Long, List<PropagationStatus>> result = new AbstractMap.SimpleEntry<>(
                 created.getResult().getKey(), propagationReporter.getStatuses());
         return result;
     }
@@ -149,7 +150,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
             }
         }
 
-        Map.Entry<Long, List<PropagationStatus>> result = new AbstractMap.SimpleEntry<Long, List<PropagationStatus>>(
+        Map.Entry<Long, List<PropagationStatus>> result = new AbstractMap.SimpleEntry<>(
                 updated.getResult().getKey().getKey(), propagationReporter.getStatuses());
         return result;
     }
@@ -198,11 +199,11 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         if (statusMod.isOnSyncope()) {
             updated = uwfAdapter.activate(user.getKey(), statusMod.getToken());
         } else {
-            updated = new WorkflowResult<Long>(user.getKey(), null, statusMod.getType().name().toLowerCase());
+            updated = new WorkflowResult<>(user.getKey(), null, statusMod.getType().name().toLowerCase());
         }
 
         List<PropagationStatus> statuses = propagateStatus(user, statusMod);
-        return new AbstractMap.SimpleEntry<Long, List<PropagationStatus>>(updated.getResult(), statuses);
+        return new AbstractMap.SimpleEntry<>(updated.getResult(), statuses);
     }
 
     @Override
@@ -211,11 +212,11 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         if (statusMod.isOnSyncope()) {
             updated = uwfAdapter.reactivate(user.getKey());
         } else {
-            updated = new WorkflowResult<Long>(user.getKey(), null, statusMod.getType().name().toLowerCase());
+            updated = new WorkflowResult<>(user.getKey(), null, statusMod.getType().name().toLowerCase());
         }
 
         List<PropagationStatus> statuses = propagateStatus(user, statusMod);
-        return new AbstractMap.SimpleEntry<Long, List<PropagationStatus>>(updated.getResult(), statuses);
+        return new AbstractMap.SimpleEntry<>(updated.getResult(), statuses);
     }
 
     @Override
@@ -224,15 +225,15 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         if (statusMod.isOnSyncope()) {
             updated = uwfAdapter.suspend(user.getKey());
         } else {
-            updated = new WorkflowResult<Long>(user.getKey(), null, statusMod.getType().name().toLowerCase());
+            updated = new WorkflowResult<>(user.getKey(), null, statusMod.getType().name().toLowerCase());
         }
 
         List<PropagationStatus> statuses = propagateStatus(user, statusMod);
-        return new AbstractMap.SimpleEntry<Long, List<PropagationStatus>>(updated.getResult(), statuses);
+        return new AbstractMap.SimpleEntry<>(updated.getResult(), statuses);
     }
 
     protected List<PropagationStatus> propagateStatus(final User user, final StatusMod statusMod) {
-        Set<String> resourcesToBeExcluded = new HashSet<String>(user.getResourceNames());
+        Set<String> resourcesToBeExcluded = new HashSet<>(user.getResourceNames());
         resourcesToBeExcluded.removeAll(statusMod.getResourceNames());
 
         List<PropagationTask> tasks = propagationManager.getUserUpdateTaskIds(
@@ -261,7 +262,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
 
             final List<PropagationTask> tasks = propagationManager.getUserUpdateTaskIds(
                     new WorkflowResult<Map.Entry<UserMod, Boolean>>(
-                            new AbstractMap.SimpleEntry<UserMod, Boolean>(userMod, Boolean.FALSE),
+                            new AbstractMap.SimpleEntry<>(userMod, Boolean.FALSE),
                             updated.getPropByRes(), updated.getPerformedTasks()));
 
             taskExecutor.execute(tasks);
@@ -276,7 +277,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         noPropResourceName.removeAll(resources);
 
         final List<PropagationTask> tasks =
-                propagationManager.getUserDeleteTaskIds(userKey, new HashSet<String>(resources), noPropResourceName);
+                propagationManager.getUserDeleteTaskIds(userKey, new HashSet<>(resources), noPropResourceName);
         final PropagationReporter propagationReporter =
                 ApplicationContextProvider.getApplicationContext().getBean(PropagationReporter.class);
         try {
@@ -303,7 +304,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
             result.setMessage("Update failed, trying to sync status anyway (if configured)\n" + e.getMessage());
 
             updated = new WorkflowResult<Map.Entry<UserMod, Boolean>>(
-                    new AbstractMap.SimpleEntry<UserMod, Boolean>(userMod, false), new PropagationByResource(),
+                    new AbstractMap.SimpleEntry<>(userMod, false), new PropagationByResource(),
                     new HashSet<String>());
         }
 
@@ -341,7 +342,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
             propagationReporter.onPrimaryResourceFailure(tasks);
         }
 
-        return new AbstractMap.SimpleEntry<Long, List<PropagationStatus>>(updated.getResult().getKey().getKey(),
+        return new AbstractMap.SimpleEntry<>(updated.getResult().getKey().getKey(),
                 propagationReporter.getStatuses());
 
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/UserSuspenderImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/UserSuspenderImpl.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/UserSuspenderImpl.java
new file mode 100644
index 0000000..357c15f
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/UserSuspenderImpl.java
@@ -0,0 +1,51 @@
+/*
+ * 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.server.provisioning.java;
+
+import org.apache.syncope.server.provisioning.api.UserSuspender;
+import org.apache.syncope.server.persistence.api.entity.user.User;
+import org.apache.syncope.server.provisioning.api.UserProvisioningManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class UserSuspenderImpl implements UserSuspender {
+
+    private static final Logger LOG = LoggerFactory.getLogger(UserSuspenderImpl.class);
+
+    @Autowired
+    private UserProvisioningManager provisioningManager;
+
+    @Override
+    public void suspend(final User user, final boolean suspend) {
+        try {
+            LOG.debug("User {}:{} is over to max failed logins", user.getKey(), user.getUsername());
+
+            // reduce failed logins number to avoid multiple request
+            user.setFailedLogins(user.getFailedLogins() - 1);
+
+            // disable user and propagate suspension if and only if it is required by policy          
+            provisioningManager.innerSuspend(user, suspend);
+        } catch (Exception e) {
+            LOG.error("Error during user suspension", e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/AbstractAttributableDataBinder.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/AbstractAttributableDataBinder.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/AbstractAttributableDataBinder.java
index c38edfc..f7e75c0 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/AbstractAttributableDataBinder.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/AbstractAttributableDataBinder.java
@@ -215,7 +215,7 @@ abstract class AbstractAttributableDataBinder {
     }
 
     private boolean evaluateMandatoryCondition(final AttributableUtil attrUtil, final ExternalResource resource,
-            final Attributable attributable, final String intAttrName, final IntMappingType intMappingType) {
+            final Attributable<?,?,?> attributable, final String intAttrName, final IntMappingType intMappingType) {
 
         boolean result = false;
 
@@ -409,7 +409,7 @@ abstract class AbstractAttributableDataBinder {
         }
 
         // 3. attributes to be removed
-        for (String attributeToBeRemoved : attributableMod.getAttrsToRemove()) {
+        for (String attributeToBeRemoved : attributableMod.getPlainAttrsToRemove()) {
             PlainSchema schema = getPlainSchema(attributeToBeRemoved, attrUtil.plainSchemaClass());
             if (schema != null) {
                 PlainAttr attr = attributable.getPlainAttr(schema.getKey());
@@ -417,7 +417,7 @@ abstract class AbstractAttributableDataBinder {
                     LOG.debug("No attribute found for schema {}", schema);
                 } else {
                     String newValue = null;
-                    for (AttrMod mod : attributableMod.getAttrsToUpdate()) {
+                    for (AttrMod mod : attributableMod.getPlainAttrsToUpdate()) {
                         if (schema.getKey().equals(mod.getSchema())) {
                             newValue = mod.getValuesToBeAdded().get(0);
                         }
@@ -456,7 +456,7 @@ abstract class AbstractAttributableDataBinder {
         LOG.debug("Attributes to be removed:\n{}", propByRes);
 
         // 4. attributes to be updated
-        for (AttrMod attributeMod : attributableMod.getAttrsToUpdate()) {
+        for (AttrMod attributeMod : attributableMod.getPlainAttrsToUpdate()) {
             PlainSchema schema = getPlainSchema(attributeMod.getSchema(), attrUtil.plainSchemaClass());
             PlainAttr attr = null;
             if (schema != null) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/ReportDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/ReportDataBinderImpl.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/ReportDataBinderImpl.java
index 12c0254..6ffe2fc 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/ReportDataBinderImpl.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/ReportDataBinderImpl.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.server.provisioning.java.data;
 
+import java.util.HashSet;
+import java.util.Set;
 import org.apache.syncope.server.provisioning.api.data.ReportDataBinder;
 import org.apache.syncope.common.lib.report.AbstractReportletConf;
 import org.apache.syncope.common.lib.report.ReportletConf;
@@ -59,7 +61,17 @@ public class ReportDataBinderImpl implements ReportDataBinder {
     @Override
     public void getReport(final Report report, final ReportTO reportTO) {
         BeanUtils.copyProperties(reportTO, report, IGNORE_REPORT_PROPERTIES);
-        report.getReportletConfs().clear();
+
+        // 1. remove all reportlet confs
+        Set<ReportletConf> toRemove = new HashSet<>();
+        for (ReportletConf conf : report.getReportletConfs()) {
+            toRemove.add(conf);
+        }
+        for (ReportletConf conf : toRemove) {
+            report.removeReportletConf(conf);
+        }
+
+        // 2. take all reportlet confs from reportTO
         for (ReportletConf conf : reportTO.getReportletConfs()) {
             report.addReportletConf(conf);
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/RoleDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/RoleDataBinderImpl.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/RoleDataBinderImpl.java
index be16ee7..851799c 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/RoleDataBinderImpl.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/RoleDataBinderImpl.java
@@ -29,7 +29,6 @@ import org.apache.syncope.common.lib.types.AttributableType;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.server.persistence.api.dao.EntitlementDAO;
-import org.apache.syncope.server.persistence.api.dao.RoleDAO;
 import org.apache.syncope.server.persistence.api.entity.AccountPolicy;
 import org.apache.syncope.server.persistence.api.entity.AttrTemplate;
 import org.apache.syncope.server.persistence.api.entity.Entitlement;
@@ -64,9 +63,6 @@ import org.springframework.transaction.annotation.Transactional;
 public class RoleDataBinderImpl extends AbstractAttributableDataBinder implements RoleDataBinder {
 
     @Autowired
-    private RoleDAO roleDAO;
-
-    @Autowired
     private ConnObjectUtil connObjectUtil;
 
     @Autowired
@@ -76,7 +72,7 @@ public class RoleDataBinderImpl extends AbstractAttributableDataBinder implement
             final Role role, final List<String> schemaNames,
             final Class<T> templateClass, final Class<S> schemaClass) {
 
-        List<T> toRemove = new ArrayList<T>();
+        List<T> toRemove = new ArrayList<>();
         for (T template : role.getAttrTemplates(templateClass)) {
             if (!schemaNames.contains(template.getSchema().getKey())) {
                 toRemove.add(template);
@@ -89,7 +85,7 @@ public class RoleDataBinderImpl extends AbstractAttributableDataBinder implement
                 S schema = getSchema(schemaName, schemaClass);
                 if (schema != null) {
                     try {
-                        T template = templateClass.newInstance();
+                        T template = entityFactory.newEntity(templateClass);
                         template.setSchema(schema);
                         template.setOwner(role);
                         role.getAttrTemplates(templateClass).add(template);
@@ -105,7 +101,7 @@ public class RoleDataBinderImpl extends AbstractAttributableDataBinder implement
     public Role create(final Role role, final RoleTO roleTO) {
         role.setInheritOwner(roleTO.isInheritOwner());
 
-        role.setInheritPlainAttrs(roleTO.isInheritAttrs());
+        role.setInheritPlainAttrs(roleTO.isInheritPlainAttrs());
         role.setInheritDerAttrs(roleTO.isInheritDerAttrs());
         role.setInheritVirAttrs(roleTO.isInheritVirAttrs());
 
@@ -335,7 +331,7 @@ public class RoleDataBinderImpl extends AbstractAttributableDataBinder implement
 
         roleTO.setInheritTemplates(role.isInheritTemplates());
 
-        roleTO.setInheritAttrs(role.isInheritPlainAttrs());
+        roleTO.setInheritPlainAttrs(role.isInheritPlainAttrs());
         roleTO.setInheritDerAttrs(role.isInheritDerAttrs());
         roleTO.setInheritVirAttrs(role.isInheritVirAttrs());
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/SchemaDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/SchemaDataBinderImpl.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/SchemaDataBinderImpl.java
index 94b0ccf..31141c0 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/SchemaDataBinderImpl.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/SchemaDataBinderImpl.java
@@ -69,15 +69,13 @@ public class SchemaDataBinderImpl implements SchemaDataBinder {
         List<PlainAttr> attrs = schemaDAO.findAttrs(schema, attributableUtil.plainAttrClass());
         if (!attrs.isEmpty()) {
             if (schema.getType() != schemaTO.getType()) {
-                SyncopeClientException e = SyncopeClientException.build(
-                        ClientExceptionType.valueOf("Invalid" + schema.getClass().getSimpleName()));
+                SyncopeClientException e = SyncopeClientException.build(ClientExceptionType.InvalidPlainSchema);
                 e.getElements().add("Cannot change type since " + schema.getKey() + " has attributes");
 
                 scce.addException(e);
             }
             if (schema.isUniqueConstraint() != schemaTO.isUniqueConstraint()) {
-                SyncopeClientException e = SyncopeClientException.build(ClientExceptionType.valueOf("Invalid"
-                        + schema.getClass().getSimpleName()));
+                SyncopeClientException e = SyncopeClientException.build(ClientExceptionType.InvalidPlainSchema);
                 e.getElements().add("Cannot alter unique contraint since " + schema.getKey() + " has attributes");
 
                 scce.addException(e);
@@ -112,11 +110,10 @@ public class SchemaDataBinderImpl implements SchemaDataBinder {
 
             scce.addException(requiredValuesMissing);
         } else if (!JexlUtil.isExpressionValid(derSchemaTO.getExpression())) {
-            SyncopeClientException invalidMandatoryCondition = SyncopeClientException.build(
-                    ClientExceptionType.InvalidValues);
-            invalidMandatoryCondition.getElements().add(derSchemaTO.getExpression());
+            SyncopeClientException e = SyncopeClientException.build(ClientExceptionType.InvalidValues);
+            e.getElements().add(derSchemaTO.getExpression());
 
-            scce.addException(invalidMandatoryCondition);
+            scce.addException(e);
         }
 
         if (scce.hasExceptions()) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/TaskDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/TaskDataBinderImpl.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/TaskDataBinderImpl.java
index 9ebfbce..920d294 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/TaskDataBinderImpl.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/data/TaskDataBinderImpl.java
@@ -180,7 +180,7 @@ public class TaskDataBinderImpl implements TaskDataBinder {
         final Class<? extends AbstractTaskTO> taskTOClass = taskUtil.taskTOClass();
 
         if (taskTOClass == null || !taskTOClass.equals(taskTO.getClass())) {
-            throw new ClassCastException(
+            throw new IllegalArgumentException(
                     String.format("taskUtil is type %s but task is not: %s", taskTOClass, taskTO.getClass()));
         }
 
@@ -211,13 +211,13 @@ public class TaskDataBinderImpl implements TaskDataBinder {
         Class<? extends Task> taskClass = taskUtil.taskClass();
         Class<? extends AbstractTaskTO> taskTOClass = taskUtil.taskTOClass();
 
-        if (taskClass == null || !taskClass.equals(task.getClass())) {
-            throw new ClassCastException(
+        if (taskClass == null || !taskClass.isAssignableFrom(task.getClass())) {
+            throw new IllegalArgumentException(
                     String.format("taskUtil is type %s but task is not: %s", taskClass, task.getClass()));
         }
 
         if (taskTOClass == null || !taskTOClass.equals(taskTO.getClass())) {
-            throw new ClassCastException(
+            throw new IllegalArgumentException(
                     String.format("taskUtil is type %s but task is not: %s", taskTOClass, taskTO.getClass()));
         }
 
@@ -283,7 +283,7 @@ public class TaskDataBinderImpl implements TaskDataBinder {
         switch (taskUtil.getType()) {
             case PROPAGATION:
                 if (!(task instanceof PropagationTask)) {
-                    throw new ClassCastException("taskUtil is type Propagation but task is not PropagationTask: "
+                    throw new IllegalArgumentException("taskUtil is type Propagation but task is not PropagationTask: "
                             + task.getClass().getName());
                 }
                 ((PropagationTaskTO) taskTO).setResource(((PropagationTask) task).getResource().getKey());
@@ -291,7 +291,7 @@ public class TaskDataBinderImpl implements TaskDataBinder {
 
             case SCHEDULED:
                 if (!(task instanceof SchedTask)) {
-                    throw new ClassCastException("taskUtil is type Sched but task is not SchedTask: "
+                    throw new IllegalArgumentException("taskUtil is type Sched but task is not SchedTask: "
                             + task.getClass().getName());
                 }
                 setExecTime((SchedTaskTO) taskTO, task);
@@ -301,7 +301,7 @@ public class TaskDataBinderImpl implements TaskDataBinder {
 
             case SYNCHRONIZATION:
                 if (!(task instanceof SyncTask)) {
-                    throw new ClassCastException("taskUtil is type Sync but task is not SyncTask: "
+                    throw new IllegalArgumentException("taskUtil is type Sync but task is not SyncTask: "
                             + task.getClass().getName());
                 }
                 setExecTime((SchedTaskTO) taskTO, task);
@@ -316,7 +316,7 @@ public class TaskDataBinderImpl implements TaskDataBinder {
 
             case PUSH:
                 if (!(task instanceof PushTask)) {
-                    throw new ClassCastException("taskUtil is type Push but task is not PushTask: "
+                    throw new IllegalArgumentException("taskUtil is type Push but task is not PushTask: "
                             + task.getClass().getName());
                 }
                 setExecTime((SchedTaskTO) taskTO, task);

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/job/SpringBeanJobFactory.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/job/SpringBeanJobFactory.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/job/SpringBeanJobFactory.java
new file mode 100644
index 0000000..b56e920
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/job/SpringBeanJobFactory.java
@@ -0,0 +1,97 @@
+/*
+ * 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.server.provisioning.java.job;
+
+import org.apache.syncope.server.provisioning.api.job.JobNamer;
+import org.apache.syncope.server.provisioning.api.job.JobInstanceLoader;
+import org.quartz.SchedulerContext;
+import org.quartz.spi.TriggerFiredBundle;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.PropertyAccessorFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
+
+public class SpringBeanJobFactory extends org.springframework.scheduling.quartz.SpringBeanJobFactory {
+
+    private String[] ignoredUnknownProperties;
+
+    private SchedulerContext schedulerContext;
+
+    @Override
+    public void setIgnoredUnknownProperties(final String[] ignoredUnknownProperties) {
+        String[] defensiveCopy = ignoredUnknownProperties.clone();
+        super.setIgnoredUnknownProperties(defensiveCopy);
+        this.ignoredUnknownProperties = defensiveCopy;
+    }
+
+    @Override
+    public void setSchedulerContext(final SchedulerContext schedulerContext) {
+        super.setSchedulerContext(schedulerContext);
+        this.schedulerContext = schedulerContext;
+    }
+
+    /**
+     * An implementation of SpringBeanJobFactory that retrieves the bean from the Spring context so that autowiring and
+     * transactions work.
+     *
+     * {@inheritDoc}
+     */
+    @Override
+    protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
+        final ApplicationContext ctx = ((ConfigurableApplicationContext) schedulerContext.get("applicationContext"));
+
+        // Try to re-create job bean from underlying task (useful for managing failover scenarios)
+        if (!ctx.containsBean(bundle.getJobDetail().getKey().getName())) {
+            Long taskId = JobNamer.getTaskIdFromJobName(bundle.getJobDetail().getKey().getName());
+            if (taskId != null) {
+                JobInstanceLoader jobInstanceLoader = ctx.getBean(JobInstanceLoader.class);
+                jobInstanceLoader.registerTaskJob(taskId);
+            }
+
+            Long reportId = JobNamer.getReportIdFromJobName(bundle.getJobDetail().getKey().getName());
+            if (reportId != null) {
+                JobInstanceLoader jobInstanceLoader = ctx.getBean(JobInstanceLoader.class);
+                jobInstanceLoader.registerReportJob(reportId);
+            }
+        }
+
+        final Object job = ctx.getBean(bundle.getJobDetail().getKey().getName());
+        final BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(job);
+        if (isEligibleForPropertyPopulation(wrapper.getWrappedInstance())) {
+            final MutablePropertyValues pvs = new MutablePropertyValues();
+            if (this.schedulerContext != null) {
+                pvs.addPropertyValues(this.schedulerContext);
+            }
+            pvs.addPropertyValues(bundle.getJobDetail().getJobDataMap());
+            pvs.addPropertyValues(bundle.getTrigger().getJobDataMap());
+            if (this.ignoredUnknownProperties == null) {
+                wrapper.setPropertyValues(pvs, true);
+            } else {
+                for (String propName : this.ignoredUnknownProperties) {
+                    if (pvs.contains(propName) && !wrapper.isWritableProperty(propName)) {
+                        pvs.removePropertyValue(propName);
+                    }
+                }
+                wrapper.setPropertyValues(pvs);
+            }
+        }
+        return job;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/notification/NotificationManager.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/notification/NotificationManager.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/notification/NotificationManager.java
index 59379b9..436f32c 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/notification/NotificationManager.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/notification/NotificationManager.java
@@ -350,7 +350,7 @@ public class NotificationManager {
                 email = user.getUsername();
                 break;
 
-            case UserSchema:
+            case UserPlainSchema:
                 UPlainAttr attr = user.getPlainAttr(recipientAttrName);
                 if (attr != null && !attr.getValuesAsStrings().isEmpty()) {
                     email = attr.getValuesAsStrings().get(0);

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/propagation/PriorityPropagationTaskExecutor.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
index 7016a0b..6d7bf38 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
@@ -76,7 +76,7 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
                     null,
                     null,
                     result,
-                    reporter.getStatuses(),
+                    reporter == null ? null : reporter.getStatuses(),
                     tasks);
 
             auditManager.audit(
@@ -85,7 +85,7 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
                     null,
                     null,
                     result,
-                    reporter.getStatuses(),
+                    reporter == null ? null : reporter.getStatuses(),
                     tasks);
         }
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractProvisioningJob.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractProvisioningJob.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractProvisioningJob.java
index f99e179..311c607 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractProvisioningJob.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractProvisioningJob.java
@@ -20,6 +20,7 @@ package org.apache.syncope.server.provisioning.java.sync;
 
 import java.lang.reflect.ParameterizedType;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import org.apache.syncope.common.lib.types.TraceLevel;
 import org.apache.syncope.server.persistence.api.dao.EntitlementDAO;
@@ -97,7 +98,7 @@ public abstract class AbstractProvisioningJob<T extends ProvisioningTask, A exte
      * @param dryRun dry run?
      * @return report as string
      */
-    protected String createReport(final List<ProvisioningResult> provResults, final TraceLevel syncTraceLevel,
+    protected String createReport(final Collection<ProvisioningResult> provResults, final TraceLevel syncTraceLevel,
             final boolean dryRun) {
 
         if (syncTraceLevel == TraceLevel.NONE) {
@@ -341,7 +342,9 @@ public abstract class AbstractProvisioningJob<T extends ProvisioningTask, A exte
                     uMapping,
                     rMapping,
                     dryRun);
-
+        } catch (Throwable t) {
+            LOG.error("While executing provisioning job {}", getClass().getName(), t);
+            throw t;
         } finally {
             // POST: clean up the SecurityContextHolder
             SecurityContextHolder.clearContext();


[10/15] syncope git commit: FIT server integration tests

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/UserITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/UserITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/UserITCase.java
new file mode 100644
index 0000000..96ca3de
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/UserITCase.java
@@ -0,0 +1,2442 @@
+/*
+ * 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.server.reference;
+
+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.io.IOException;
+import java.io.InputStream;
+import java.security.AccessControlException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import javax.naming.NamingException;
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.Response;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.common.util.Base64Utility;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.AttributableOperations;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.mod.AttrMod;
+import org.apache.syncope.common.lib.mod.MembershipMod;
+import org.apache.syncope.common.lib.mod.ResourceAssociationMod;
+import org.apache.syncope.common.lib.mod.StatusMod;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.BulkAction;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.BulkActionResult.Status;
+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.PagedResult;
+import org.apache.syncope.common.lib.to.PasswordPolicyTO;
+import org.apache.syncope.common.lib.to.PropagationStatus;
+import org.apache.syncope.common.lib.to.PropagationTaskTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.CipherAlgorithm;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.PolicyType;
+import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.common.lib.types.ResourceAssociationActionType;
+import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
+import org.apache.syncope.common.lib.types.SubjectType;
+import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.common.lib.wrap.ResourceName;
+import org.apache.syncope.common.rest.api.CollectionWrapper;
+import org.apache.syncope.common.rest.api.Preference;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.PolicyService;
+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.server.misc.security.Encryptor;
+import org.apache.syncope.server.provisioning.java.propagation.DBPasswordPropagationActions;
+import org.apache.syncope.server.provisioning.java.propagation.LDAPPasswordPropagationActions;
+import org.identityconnectors.framework.common.objects.Name;
+import org.identityconnectors.framework.common.objects.OperationalAttributes;
+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 UserITCase extends AbstractITCase {
+
+    private String getStringAttribute(final ConnObjectTO connObjectTO, final String attrName) {
+        return connObjectTO.getPlainAttrMap().get(attrName).getValues().get(0);
+    }
+
+    private boolean getBooleanAttribute(final ConnObjectTO connObjectTO, final String attrName) {
+        return Boolean.parseBoolean(getStringAttribute(connObjectTO, attrName));
+    }
+
+    public static UserTO getUniqueSampleTO(final String email) {
+        return getSampleTO(getUUIDString() + email);
+    }
+
+    public static UserTO getSampleTO(final String email) {
+        String uid = email;
+        UserTO userTO = new UserTO();
+        userTO.setPassword("password123");
+        userTO.setUsername(uid);
+
+        userTO.getPlainAttrs().add(attrTO("fullname", uid));
+        userTO.getPlainAttrs().add(attrTO("firstname", uid));
+        userTO.getPlainAttrs().add(attrTO("surname", "surname"));
+        userTO.getPlainAttrs().add(attrTO("type", "a type"));
+        userTO.getPlainAttrs().add(attrTO("userId", uid));
+        userTO.getPlainAttrs().add(attrTO("email", uid));
+        userTO.getPlainAttrs().add(attrTO("loginDate", new SimpleDateFormat("yyyy-MM-dd").format(new Date())));
+        userTO.getDerAttrs().add(attrTO("cn", null));
+        userTO.getVirAttrs().add(attrTO("virtualdata", "virtualvalue"));
+        return userTO;
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void createUserWithNoPropagation() {
+        // get task list
+        PagedResult<PropagationTaskTO> tasks = taskService.list(TaskType.PROPAGATION, 1, 1);
+        assertNotNull(tasks);
+        assertFalse(tasks.getResult().isEmpty());
+
+        long maxKey = tasks.getResult().iterator().next().getKey();
+
+        // create a new user
+        UserTO userTO = getUniqueSampleTO("xxx@xxx.xxx");
+
+        userTO.setPassword("password123");
+        userTO.getResources().add(RESOURCE_NAME_NOPROPAGATION);
+
+        createUser(userTO);
+
+        // get the new task list
+        tasks = taskService.list(TaskType.PROPAGATION, 1, 1);
+        assertNotNull(tasks);
+        assertFalse(tasks.getResult().isEmpty());
+
+        long newMaxId = tasks.getResult().iterator().next().getKey();
+
+        assertTrue(newMaxId > maxKey);
+
+        // get last task
+        PropagationTaskTO taskTO = taskService.read(newMaxId);
+
+        assertNotNull(taskTO);
+        assertTrue(taskTO.getExecutions().isEmpty());
+    }
+
+    @Test
+    public void issue172() {
+        List<PasswordPolicyTO> policies = policyService.list(PolicyType.GLOBAL_PASSWORD);
+        for (PasswordPolicyTO policyTO : policies) {
+            policyService.delete(policyTO.getKey());
+        }
+
+        try {
+            UserTO userTO = getUniqueSampleTO("issue172@syncope.apache.org");
+            createUser(userTO);
+        } finally {
+            for (PasswordPolicyTO policyTO : policies) {
+                Response response = policyService.create(policyTO);
+                PasswordPolicyTO cPolicyTO = getObject(
+                        response.getLocation(), PolicyService.class, PasswordPolicyTO.class);
+                assertNotNull(cPolicyTO);
+            }
+        }
+    }
+
+    @Test
+    public void issue186() {
+        // 1. create an user with strict mandatory attributes only
+        UserTO userTO = new UserTO();
+        String userId = getUUIDString() + "issue186@syncope.apache.org";
+        userTO.setUsername(userId);
+        userTO.setPassword("password");
+
+        userTO.getPlainAttrs().add(attrTO("userId", userId));
+        userTO.getPlainAttrs().add(attrTO("fullname", userId));
+        userTO.getPlainAttrs().add(attrTO("surname", userId));
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+        assertTrue(userTO.getResources().isEmpty());
+
+        // 2. update assigning a resource forcing mandatory constraints: must fail with RequiredValuesMissing
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.setPassword("newPassword");
+        userMod.getResourcesToAdd().add(RESOURCE_NAME_WS2);
+
+        try {
+            userTO = updateUser(userMod);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
+        }
+
+        // 3. update assigning a resource NOT forcing mandatory constraints
+        // AND primary: must fail with PropagationException
+        userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.setPassword("newPassword");
+        userMod.getResourcesToAdd().add(RESOURCE_NAME_WS1);
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO.getPropagationStatusTOs().get(0).getFailureReason());
+
+        // 4. update assigning a resource NOT forcing mandatory constraints
+        // BUT not primary: must succeed
+        userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.setPassword("newPassword123456");
+        userMod.getResourcesToAdd().add(RESOURCE_NAME_CSV);
+        updateUser(userMod);
+    }
+
+    @Test
+    public void enforceMandatoryCondition() {
+        UserTO userTO = getUniqueSampleTO("enforce@apache.org");
+        userTO.getResources().add(RESOURCE_NAME_WS2);
+        userTO.setPassword("newPassword");
+
+        AttrTO type = null;
+        for (AttrTO attr : userTO.getPlainAttrs()) {
+            if ("type".equals(attr.getSchema())) {
+                type = attr;
+            }
+        }
+        assertNotNull(type);
+        userTO.getPlainAttrs().remove(type);
+
+        try {
+            userTO = createUser(userTO);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
+        }
+
+        userTO.getPlainAttrs().add(type);
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+    }
+
+    @Test
+    public void enforceMandatoryConditionOnDerived() {
+        ResourceTO resourceTO = resourceService.read(RESOURCE_NAME_CSV);
+        assertNotNull(resourceTO);
+        resourceTO.setKey("resource-csv-enforcing");
+        resourceTO.setEnforceMandatoryCondition(true);
+
+        Response response = resourceService.create(resourceTO);
+        resourceTO = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+        assertNotNull(resourceTO);
+
+        UserTO userTO = getUniqueSampleTO("syncope222@apache.org");
+        userTO.getResources().add(resourceTO.getKey());
+        userTO.setPassword("newPassword");
+
+        try {
+            userTO = createUser(userTO);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
+        }
+
+        userTO.getDerAttrs().add(attrTO("csvuserid", null));
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+        assertEquals(Collections.singleton("resource-csv-enforcing"), userTO.getResources());
+    }
+
+    @Test
+    public void createUserWithDbPropagation() {
+        UserTO userTO = getUniqueSampleTO("yyy@yyy.yyy");
+        userTO.getResources().add(RESOURCE_NAME_TESTDB);
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+        assertEquals(1, userTO.getPropagationStatusTOs().size());
+        assertTrue(userTO.getPropagationStatusTOs().get(0).getStatus().isSuccessful());
+    }
+
+    @Test(expected = SyncopeClientException.class)
+    public void createWithInvalidPassword() {
+        UserTO userTO = getSampleTO("invalidpasswd@syncope.apache.org");
+        userTO.setPassword("pass");
+        createUser(userTO);
+    }
+
+    @Test(expected = SyncopeClientException.class)
+    public void createWithInvalidUsername() {
+        UserTO userTO = getSampleTO("invalidusername@syncope.apache.org");
+        userTO.setUsername("us");
+
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(7L);
+
+        userTO.getMemberships().add(membershipTO);
+
+        createUser(userTO);
+    }
+
+    @Test(expected = SyncopeClientException.class)
+    public void createWithInvalidPasswordByRes() {
+        UserTO userTO = getSampleTO("invalidPwdByRes@passwd.com");
+
+        // configured to be minLength=16
+        userTO.setPassword("password1");
+        userTO.getResources().add(RESOURCE_NAME_NOPROPAGATION);
+        createUser(userTO);
+    }
+
+    @Test(expected = SyncopeClientException.class)
+    public void createWithInvalidPasswordByRole() {
+        UserTO userTO = getSampleTO("invalidPwdByRole@passwd.com");
+
+        // configured to be minLength=16
+        userTO.setPassword("password1");
+
+        final MembershipTO membership = new MembershipTO();
+        membership.setRoleId(8L);
+
+        userTO.getMemberships().add(membership);
+
+        createUser(userTO);
+    }
+
+    @Test(expected = SyncopeClientException.class)
+    public void createWithException() {
+        UserTO newUserTO = new UserTO();
+        newUserTO.getPlainAttrs().add(attrTO("userId", "userId@nowhere.org"));
+        createUser(newUserTO);
+    }
+
+    @Test
+    public void create() {
+        // get task list
+        PagedResult<PropagationTaskTO> tasks = taskService.list(TaskType.PROPAGATION, 1, 1);
+        assertNotNull(tasks);
+        assertFalse(tasks.getResult().isEmpty());
+
+        long maxKey = tasks.getResult().iterator().next().getKey();
+        PropagationTaskTO taskTO = taskService.read(maxKey);
+
+        assertNotNull(taskTO);
+        int maxTaskExecutions = taskTO.getExecutions().size();
+
+        UserTO userTO = getUniqueSampleTO("a.b@c.com");
+
+        // add a membership
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(8L);
+        userTO.getMemberships().add(membershipTO);
+
+        // add an attribute with no values: must be ignored
+        membershipTO.getPlainAttrs().add(attrTO("subscriptionDate", null));
+
+        // add an attribute with a non-existing schema: must be ignored
+        AttrTO attrWithInvalidSchemaTO = attrTO("invalid schema", "a value");
+        userTO.getPlainAttrs().add(attrWithInvalidSchemaTO);
+
+        // add an attribute with null value: must be ignored
+        userTO.getPlainAttrs().add(attrTO("activationDate", null));
+
+        // 1. create user
+        UserTO newUserTO = createUser(userTO);
+
+        assertNotNull(newUserTO);
+
+        // issue SYNCOPE-15
+        assertNotNull(newUserTO.getCreationDate());
+        assertNotNull(newUserTO.getCreator());
+        assertNotNull(newUserTO.getLastChangeDate());
+        assertNotNull(newUserTO.getLastModifier());
+        assertEquals(newUserTO.getCreationDate(), newUserTO.getLastChangeDate());
+
+        assertFalse(newUserTO.getPlainAttrs().contains(attrWithInvalidSchemaTO));
+
+        // check for changePwdDate
+        assertNotNull(newUserTO.getCreationDate());
+
+        // 2. check for virtual attribute value
+        newUserTO = userService.read(newUserTO.getKey());
+        assertNotNull(newUserTO);
+
+        assertNotNull(newUserTO.getVirAttrMap());
+        assertNotNull(newUserTO.getVirAttrMap().get("virtualdata").getValues());
+        assertFalse(newUserTO.getVirAttrMap().get("virtualdata").getValues().isEmpty());
+        assertEquals("virtualvalue", newUserTO.getVirAttrMap().get("virtualdata").getValues().get(0));
+
+        // get the new task list
+        tasks = taskService.list(TaskType.PROPAGATION, 1, 1);
+        assertNotNull(tasks);
+        assertFalse(tasks.getResult().isEmpty());
+
+        long newMaxKey = tasks.getResult().iterator().next().getKey();
+
+        // default configuration for ws-target-resource2:
+        // only failed executions have to be registered
+        // --> no more tasks/executions should be added
+        assertEquals(newMaxKey, maxKey);
+
+        // get last task
+        taskTO = taskService.read(newMaxKey);
+
+        assertNotNull(taskTO);
+        assertEquals(maxTaskExecutions, taskTO.getExecutions().size());
+
+        // 3. verify password
+        UserSelfService userSelfService1 = clientFactory.create(
+                newUserTO.getUsername(), "password123").getService(UserSelfService.class);
+        try {
+            UserTO user = userSelfService1.read();
+            assertNotNull(user);
+        } catch (AccessControlException e) {
+            fail("Credentials should be valid and not cause AccessControlException");
+        }
+
+        UserSelfService userSelfService2 = clientFactory.create(
+                newUserTO.getUsername(), "passwordXX").getService(UserSelfService.class);
+        try {
+            userSelfService2.read();
+            fail("Credentials are invalid, thus request should raise AccessControlException");
+        } catch (AccessControlException e) {
+            assertNotNull(e);
+        }
+
+        // 4. try (and fail) to create another user with same (unique) values
+        userTO = getSampleTO(userTO.getUsername());
+        AttrTO userIdAttr = userTO.getPlainAttrMap().get("userId");
+        userIdAttr.getValues().clear();
+        userIdAttr.getValues().add("a.b@c.com");
+
+        try {
+            createUser(userTO);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.DataIntegrityViolation, e.getType());
+        }
+    }
+
+    @Test
+    public void createWithRequiredValueMissing() {
+        UserTO userTO = getSampleTO("a.b@c.it");
+
+        AttrTO type = userTO.getPlainAttrMap().get("type");
+        userTO.getPlainAttrs().remove(type);
+
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(8L);
+        userTO.getMemberships().add(membershipTO);
+
+        // 1. create user without type (mandatory by UserSchema)
+        try {
+            createUser(userTO);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
+        }
+
+        userTO.getPlainAttrs().add(attrTO("type", "F"));
+
+        AttrTO surname = userTO.getPlainAttrMap().get("surname");
+        userTO.getPlainAttrs().remove(surname);
+
+        // 2. create user without surname (mandatory when type == 'F')
+        try {
+            createUser(userTO);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
+        }
+    }
+
+    @Test
+    public void delete() {
+        try {
+            userService.delete(0L);
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
+        }
+
+        UserTO userTO = getSampleTO("qqgf.z@nn.com");
+
+        // specify a propagation
+        userTO.getResources().add(RESOURCE_NAME_TESTDB);
+
+        userTO = createUser(userTO);
+
+        long id = userTO.getKey();
+
+        userTO = deleteUser(id);
+
+        assertNotNull(userTO);
+        assertEquals(id, userTO.getKey());
+        assertTrue(userTO.getPlainAttrs().isEmpty());
+
+        // check for propagation result
+        assertFalse(userTO.getPropagationStatusTOs().isEmpty());
+        assertTrue(userTO.getPropagationStatusTOs().get(0).getStatus().isSuccessful());
+
+        try {
+            userService.delete(userTO.getKey());
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
+        }
+    }
+
+    @Test
+    public void deleteByUsername() {
+        UserTO userTO = getSampleTO("delete.by.username@apache.org");
+
+        // specify a propagation
+        userTO.getResources().add(RESOURCE_NAME_TESTDB);
+
+        userTO = createUser(userTO);
+
+        long id = userTO.getKey();
+        userTO = userService.read(id);
+        userTO = deleteUser(userTO.getKey());
+
+        assertNotNull(userTO);
+        assertEquals(id, userTO.getKey());
+        assertTrue(userTO.getPlainAttrs().isEmpty());
+
+        // check for propagation result
+        assertFalse(userTO.getPropagationStatusTOs().isEmpty());
+        assertTrue(userTO.getPropagationStatusTOs().get(0).getStatus().isSuccessful());
+
+        try {
+            userService.read(userTO.getKey());
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
+        }
+    }
+
+    @Test
+    public void list() {
+        PagedResult<UserTO> users = userService.list();
+        assertNotNull(users);
+        assertFalse(users.getResult().isEmpty());
+
+        for (UserTO user : users.getResult()) {
+            assertNotNull(user);
+        }
+    }
+
+    @Test
+    public void paginatedList() {
+        PagedResult<UserTO> users = userService.list(1, 2);
+        assertNotNull(users);
+        assertFalse(users.getResult().isEmpty());
+        assertEquals(2, users.getResult().size());
+
+        for (UserTO user : users.getResult()) {
+            assertNotNull(user);
+        }
+
+        users = userService.list(2, 2);
+        assertNotNull(users);
+        assertFalse(users.getResult().isEmpty());
+        assertEquals(2, users.getResult().size());
+
+        users = userService.list(100, 2);
+        assertNotNull(users);
+        assertTrue(users.getResult().isEmpty());
+    }
+
+    @Test
+    public void read() {
+        UserTO userTO = userService.read(1L);
+
+        assertNotNull(userTO);
+        assertNotNull(userTO.getPlainAttrs());
+        assertFalse(userTO.getPlainAttrs().isEmpty());
+    }
+
+    @Test
+    public void readWithMailAddressAsUserName() {
+        UserTO userTO = createUser(getUniqueSampleTO("mail@domain.org"));
+        userTO = userService.read(userTO.getKey());
+        assertNotNull(userTO);
+    }
+
+    @Test
+    public void updateWithouPassword() {
+        UserTO userTO = getUniqueSampleTO("updatewithout@password.com");
+
+        userTO = createUser(userTO);
+
+        assertNotNull(userTO);
+
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.getDerAttrsToRemove().add("cn");
+
+        userTO = updateUser(userMod);
+
+        assertNotNull(userTO);
+        assertNotNull(userTO.getDerAttrMap());
+        assertFalse(userTO.getDerAttrMap().containsKey("cn"));
+    }
+
+    @Test(expected = SyncopeClientException.class)
+    public void updateInvalidPassword() {
+        UserTO userTO = getSampleTO("updateinvalid@password.com");
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.setPassword("pass");
+
+        userService.update(userMod.getKey(), userMod);
+    }
+
+    @Test(expected = SyncopeClientException.class)
+    public void updateSamePassword() {
+        UserTO userTO = getSampleTO("updatesame@password.com");
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.setPassword("password123");
+
+        userService.update(userMod.getKey(), userMod);
+    }
+
+    @Test
+    public void update() {
+        UserTO userTO = getUniqueSampleTO("g.h@t.com");
+
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(8L);
+        membershipTO.getPlainAttrs().add(attrTO("subscriptionDate", "2009-08-18T16:33:12.203+0200"));
+        userTO.getMemberships().add(membershipTO);
+
+        userTO = createUser(userTO);
+
+        assertFalse(userTO.getDerAttrs().isEmpty());
+        assertEquals(1, userTO.getMemberships().size());
+
+        MembershipMod membershipMod = new MembershipMod();
+        membershipMod.setRole(8L);
+        membershipMod.getPlainAttrsToUpdate().add(attrMod("subscriptionDate", "2010-08-18T16:33:12.203+0200"));
+
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.setPassword("new2Password");
+
+        userMod.getPlainAttrsToRemove().add("userId");
+        String newUserId = getUUIDString() + "t.w@spre.net";
+        userMod.getPlainAttrsToUpdate().add(attrMod("userId", newUserId));
+
+        userMod.getPlainAttrsToRemove().add("fullname");
+        String newFullName = getUUIDString() + "g.h@t.com";
+        userMod.getPlainAttrsToUpdate().add(attrMod("fullname", newFullName));
+
+        userMod.getDerAttrsToAdd().add("cn");
+        userMod.getMembershipsToAdd().add(membershipMod);
+        userMod.getMembershipsToRemove().add(userTO.getMemberships().iterator().next().getKey());
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+
+        // issue SYNCOPE-15
+        assertNotNull(userTO.getCreationDate());
+        assertNotNull(userTO.getCreator());
+        assertNotNull(userTO.getLastChangeDate());
+        assertNotNull(userTO.getLastModifier());
+        assertTrue(userTO.getCreationDate().before(userTO.getLastChangeDate()));
+
+        assertEquals(1, userTO.getMemberships().size());
+        assertEquals(1, userTO.getMemberships().iterator().next().getPlainAttrs().size());
+        assertFalse(userTO.getDerAttrs().isEmpty());
+
+        AttrTO userIdAttr = userTO.getPlainAttrMap().get("userId");
+        assertEquals(Collections.singletonList(newUserId), userIdAttr.getValues());
+
+        AttrTO fullNameAttr = userTO.getPlainAttrMap().get("fullname");
+        assertEquals(Collections.singletonList(newFullName), fullNameAttr.getValues());
+    }
+
+    @Test
+    public void updatePasswordOnly() {
+        int beforeTasks = taskService.list(TaskType.PROPAGATION, 1, 1).getTotalCount();
+        assertFalse(beforeTasks <= 0);
+
+        UserTO userTO = getUniqueSampleTO("pwdonly@t.com");
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(8L);
+        membershipTO.getPlainAttrs().add(attrTO("subscriptionDate", "2009-08-18T16:33:12.203+0200"));
+        userTO.getMemberships().add(membershipTO);
+
+        userTO = createUser(userTO);
+
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.setPassword("newPassword123");
+
+        userTO = updateUser(userMod);
+
+        // check for changePwdDate
+        assertNotNull(userTO.getChangePwdDate());
+
+        int afterTasks = taskService.list(TaskType.PROPAGATION, 1, 1).getTotalCount();
+        assertFalse(beforeTasks <= 0);
+
+        assertTrue(beforeTasks < afterTasks);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void verifyTaskRegistration() {
+        // get task list
+        PagedResult<PropagationTaskTO> tasks = taskService.list(TaskType.PROPAGATION, 1, 1);
+        assertNotNull(tasks);
+        assertFalse(tasks.getResult().isEmpty());
+
+        long maxKey = tasks.getResult().iterator().next().getKey();
+
+        // --------------------------------------
+        // Create operation
+        // --------------------------------------
+        UserTO userTO = getUniqueSampleTO("t@p.mode");
+
+        // add a membership
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(8L);
+        userTO.getMemberships().add(membershipTO);
+
+        // 1. create user
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        // get the new task list
+        tasks = taskService.list(TaskType.PROPAGATION, 1, 1);
+        assertNotNull(tasks);
+        assertFalse(tasks.getResult().isEmpty());
+
+        long newMaxKey = tasks.getResult().iterator().next().getKey();
+
+        // default configuration for ws-target-resource2:
+        // only failed executions have to be registered
+        // --> no more tasks/executions should be added
+        assertEquals(newMaxKey, maxKey);
+
+        // --------------------------------------
+        // Update operation
+        // --------------------------------------
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+
+        userMod.getPlainAttrsToUpdate().add(attrMod("surname", "surname"));
+
+        userTO = updateUser(userMod);
+
+        assertNotNull(userTO);
+
+        // get the new task list
+        tasks = taskService.list(TaskType.PROPAGATION, 1, 1);
+
+        maxKey = newMaxKey;
+        newMaxKey = tasks.getResult().iterator().next().getKey();
+
+        // default configuration for ws-target-resource2:
+        // all update executions have to be registered
+        assertTrue(newMaxKey > maxKey);
+
+        final PropagationTaskTO taskTO = taskService.read(newMaxKey);
+
+        assertNotNull(taskTO);
+        assertEquals(1, taskTO.getExecutions().size());
+
+        // --------------------------------------
+        // Delete operation
+        // --------------------------------------
+        userService.delete(userTO.getKey());
+
+        // get the new task list
+        tasks = taskService.list(TaskType.PROPAGATION, 1, 1);
+
+        maxKey = newMaxKey;
+        newMaxKey = tasks.getResult().iterator().next().getKey();
+
+        // default configuration for ws-target-resource2: no delete executions have to be registered
+        // --> no more tasks/executions should be added
+        assertEquals(newMaxKey, maxKey);
+    }
+
+    @Test
+    public void createActivate() {
+        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers());
+
+        UserTO userTO = getUniqueSampleTO("createActivate@syncope.apache.org");
+
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(11L);
+        userTO.getMemberships().add(membershipTO);
+
+        userTO = createUser(userTO);
+
+        assertNotNull(userTO);
+        assertNotNull(userTO.getToken());
+        assertNotNull(userTO.getTokenExpireTime());
+
+        assertEquals("created", userTO.getStatus());
+
+        StatusMod statusMod = new StatusMod();
+        statusMod.setType(StatusMod.ModType.ACTIVATE);
+        statusMod.setToken(userTO.getToken());
+        userTO = userService.status(userTO.getKey(), statusMod).readEntity(UserTO.class);
+
+        assertNotNull(userTO);
+        assertNull(userTO.getToken());
+        assertNull(userTO.getTokenExpireTime());
+        assertEquals("active", userTO.getStatus());
+    }
+
+    @Test
+    public void suspendReactivate() {
+        UserTO userTO = getUniqueSampleTO("suspendReactivate@syncope.apache.org");
+
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(7L);
+        userTO.getMemberships().add(membershipTO);
+
+        userTO = createUser(userTO);
+
+        assertNotNull(userTO);
+        assertEquals(ActivitiDetector.isActivitiEnabledForUsers()
+                ? "active"
+                : "created", userTO.getStatus());
+
+        StatusMod statusMod = new StatusMod();
+        statusMod.setType(StatusMod.ModType.SUSPEND);
+        userTO = userService.status(userTO.getKey(), statusMod).readEntity(UserTO.class);
+        assertNotNull(userTO);
+        assertEquals("suspended", userTO.getStatus());
+
+        statusMod = new StatusMod();
+        statusMod.setType(StatusMod.ModType.REACTIVATE);
+        userTO = userService.status(userTO.getKey(), statusMod).readEntity(UserTO.class);
+        assertNotNull(userTO);
+        assertEquals("active", userTO.getStatus());
+    }
+
+    @Test
+    public void suspendReactivateOnResource() {
+        // Assert resources are present
+        ResourceTO dbTable = resourceService.read(RESOURCE_NAME_TESTDB);
+        assertNotNull(dbTable);
+        ResourceTO ldap = resourceService.read(RESOURCE_NAME_LDAP);
+        assertNotNull(ldap);
+
+        // Create user with reference to resources
+        UserTO userTO = getUniqueSampleTO("suspreactonresource@syncope.apache.org");
+        userTO.getMemberships().clear();
+        userTO.getResources().clear();
+        userTO.getResources().add(RESOURCE_NAME_TESTDB);
+        userTO.getResources().add(RESOURCE_NAME_LDAP);
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+        assertEquals(ActivitiDetector.isActivitiEnabledForUsers()
+                ? "active"
+                : "created", userTO.getStatus());
+        long userId = userTO.getKey();
+
+        // Suspend with effect on syncope, ldap and db => user should be suspended in syncope and all resources
+        StatusMod statusMod = new StatusMod();
+        statusMod.setType(StatusMod.ModType.SUSPEND);
+        statusMod.setOnSyncope(true);
+        statusMod.getResourceNames().add(RESOURCE_NAME_TESTDB);
+        statusMod.getResourceNames().add(RESOURCE_NAME_LDAP);
+        userTO = userService.status(userId, statusMod).readEntity(UserTO.class);
+        assertNotNull(userTO);
+        assertEquals("suspended", userTO.getStatus());
+
+        ConnObjectTO connObjectTO = resourceService.getConnectorObject(RESOURCE_NAME_TESTDB, SubjectType.USER, userId);
+        assertFalse(getBooleanAttribute(connObjectTO, OperationalAttributes.ENABLE_NAME));
+
+        connObjectTO = resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.USER, userId);
+        assertNotNull(connObjectTO);
+
+        // Suspend and reactivate only on ldap => db and syncope should still show suspended
+        statusMod = new StatusMod();
+        statusMod.setType(StatusMod.ModType.SUSPEND);
+        statusMod.setOnSyncope(false);
+        statusMod.getResourceNames().add(RESOURCE_NAME_LDAP);
+        userService.status(userId, statusMod);
+        statusMod.setType(StatusMod.ModType.REACTIVATE);
+        userTO = userService.status(userId, statusMod).readEntity(UserTO.class);
+        assertNotNull(userTO);
+        assertEquals("suspended", userTO.getStatus());
+
+        connObjectTO = resourceService.getConnectorObject(RESOURCE_NAME_TESTDB, SubjectType.USER, userId);
+        assertFalse(getBooleanAttribute(connObjectTO, OperationalAttributes.ENABLE_NAME));
+
+        // Reactivate on syncope and db => syncope and db should show the user as active
+        statusMod = new StatusMod();
+        statusMod.setType(StatusMod.ModType.REACTIVATE);
+        statusMod.setOnSyncope(true);
+        statusMod.getResourceNames().add(RESOURCE_NAME_TESTDB);
+
+        userTO = userService.status(userId, statusMod).readEntity(UserTO.class);
+        assertNotNull(userTO);
+        assertEquals("active", userTO.getStatus());
+
+        connObjectTO = resourceService.getConnectorObject(RESOURCE_NAME_TESTDB, SubjectType.USER, userId);
+        assertTrue(getBooleanAttribute(connObjectTO, OperationalAttributes.ENABLE_NAME));
+    }
+
+    public void updateMultivalueAttribute() {
+        UserTO userTO = getSampleTO("multivalue@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getDerAttrs().clear();
+        userTO.getVirAttrs().clear();
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        AttrTO loginDate = userTO.getPlainAttrMap().get("loginDate");
+        assertNotNull(loginDate);
+        assertEquals(1, loginDate.getValues().size());
+
+        UserMod userMod = new UserMod();
+
+        AttrMod loginDateMod = new AttrMod();
+        loginDateMod.getValuesToBeAdded().add("2000-01-01");
+
+        userMod.setKey(userTO.getKey());
+        userMod.getPlainAttrsToUpdate().add(loginDateMod);
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+
+        loginDate = userTO.getPlainAttrMap().get("loginDate");
+        assertNotNull(loginDate);
+        assertEquals(2, loginDate.getValues().size());
+    }
+
+    @Test(expected = EmptyResultDataAccessException.class)
+    public void issue213() {
+        UserTO userTO = getUniqueSampleTO("issue213@syncope.apache.org");
+        userTO.getResources().add(RESOURCE_NAME_TESTDB);
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+        assertEquals(1, userTO.getResources().size());
+
+        JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+
+        String username = jdbcTemplate.queryForObject("SELECT id FROM test WHERE id=?", String.class,
+                userTO.getUsername());
+
+        assertEquals(userTO.getUsername(), username);
+
+        UserMod userMod = new UserMod();
+
+        userMod.setKey(userTO.getKey());
+        userMod.getResourcesToRemove().add(RESOURCE_NAME_TESTDB);
+
+        userTO = updateUser(userMod);
+        assertTrue(userTO.getResources().isEmpty());
+
+        jdbcTemplate.queryForObject("SELECT id FROM test WHERE id=?", String.class, userTO.getUsername());
+    }
+
+    @Test
+    public void issue234() {
+        UserTO inUserTO = getUniqueSampleTO("issue234@syncope.apache.org");
+        inUserTO.getResources().add(RESOURCE_NAME_LDAP);
+
+        UserTO userTO = createUser(inUserTO);
+        assertNotNull(userTO);
+
+        UserMod userMod = new UserMod();
+
+        userMod.setKey(userTO.getKey());
+        userMod.setUsername("1" + userTO.getUsername());
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+        assertEquals("1" + inUserTO.getUsername(), userTO.getUsername());
+    }
+
+    @Test
+    public void issue270() {
+        // 1. create a new user without virtual attributes
+        UserTO original = getUniqueSampleTO("issue270@syncope.apache.org");
+        // be sure to remove all virtual attributes
+        original.getVirAttrs().clear();
+
+        original = createUser(original);
+
+        assertNotNull(original);
+
+        assertTrue(original.getVirAttrs().isEmpty());
+
+        UserTO toBeUpdated = userService.read(original.getKey());
+
+        AttrTO virtual = attrTO("virtualdata", "virtualvalue");
+        toBeUpdated.getVirAttrs().add(virtual);
+
+        // 2. try to update by adding a resource, but no password: must fail
+        UserMod userMod = AttributableOperations.diff(toBeUpdated, original);
+        assertNotNull(userMod);
+
+        toBeUpdated = updateUser(userMod);
+        assertNotNull(toBeUpdated);
+
+        assertFalse(toBeUpdated.getVirAttrs().isEmpty());
+        assertNotNull(toBeUpdated.getVirAttrs().get(0));
+
+        assertEquals(virtual.getSchema(), toBeUpdated.getVirAttrs().get(0).getSchema());
+    }
+
+    @Test
+    public final void issue280() {
+        UserTO userTO = getUniqueSampleTO("issue280@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getDerAttrs().clear();
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.setPassword("123password");
+        userMod.getResourcesToAdd().add(RESOURCE_NAME_TESTDB);
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+
+        final List<PropagationStatus> propagations = userTO.getPropagationStatusTOs();
+
+        assertNotNull(propagations);
+        assertEquals(1, propagations.size());
+
+        final PropagationTaskExecStatus status = propagations.get(0).getStatus();
+        final String resource = propagations.get(0).getResource();
+
+        assertNotNull(status);
+        assertEquals(RESOURCE_NAME_TESTDB, resource);
+        assertTrue(status.isSuccessful());
+    }
+
+    @Test
+    public void issue281() {
+        UserTO userTO = getUniqueSampleTO("issue281@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getDerAttrs().clear();
+        userTO.getResources().add(RESOURCE_NAME_CSV);
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        final List<PropagationStatus> propagations = userTO.getPropagationStatusTOs();
+
+        assertNotNull(propagations);
+        assertEquals(1, propagations.size());
+
+        final PropagationTaskExecStatus status = propagations.get(0).getStatus();
+        final String resource = propagations.get(0).getResource();
+
+        assertNotNull(status);
+        assertEquals(RESOURCE_NAME_CSV, resource);
+        assertFalse(status.isSuccessful());
+    }
+
+    @Test
+    public void issue288() {
+        UserTO userTO = getSampleTO("issue288@syncope.apache.org");
+        userTO.getPlainAttrs().add(attrTO("aLong", "STRING"));
+
+        try {
+            createUser(userTO);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidValues, e.getType());
+        }
+    }
+
+    @Test
+    public void roleAttrPropagation() {
+        UserTO userTO = getUniqueSampleTO("checkRoleAttrPropagation@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getDerAttrs().clear();
+        userTO.getVirAttrs().clear();
+
+        userTO.getDerAttrs().add(attrTO("csvuserid", null));
+
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(1L);
+
+        userTO.getMemberships().add(membershipTO);
+
+        userTO.getResources().add(RESOURCE_NAME_CSV);
+
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+        assertNotNull(actual.getDerAttrMap().get("csvuserid"));
+
+        ConnObjectTO connObjectTO =
+                resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey());
+        assertNotNull(connObjectTO);
+        assertEquals("sx-dx", connObjectTO.getPlainAttrMap().get("ROLE").getValues().get(0));
+    }
+
+    @Test
+    public void membershipAttrPropagation() {
+        UserTO userTO = getUniqueSampleTO("checkMembAttrPropagation@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getDerAttrs().clear();
+        userTO.getVirAttrs().clear();
+        userTO.getDerAttrs().add(attrTO("csvuserid", null));
+
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(1L);
+        membershipTO.getPlainAttrs().add(attrTO("mderived_sx", "sx"));
+        membershipTO.getPlainAttrs().add(attrTO("mderived_dx", "dx"));
+        membershipTO.getDerAttrs().add(attrTO("mderToBePropagated", null));
+        userTO.getMemberships().add(membershipTO);
+
+        userTO.getResources().add(RESOURCE_NAME_CSV);
+
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+        assertNotNull(actual.getDerAttrMap().get("csvuserid"));
+
+        ConnObjectTO connObjectTO =
+                resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey());
+        assertNotNull(connObjectTO);
+        assertEquals("sx-dx", connObjectTO.getPlainAttrMap().get("MEMBERSHIP").getValues().get(0));
+    }
+
+    @Test
+    public void noContent() throws IOException {
+        SyncopeClient noContentclient = clientFactory.create(ADMIN_UNAME, ADMIN_PWD);
+        UserService noContentService = noContentclient.prefer(UserService.class, Preference.RETURN_NO_CONTENT);
+
+        UserTO user = getUniqueSampleTO("nocontent@syncope.apache.org");
+
+        Response response = noContentService.create(user, true);
+        assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
+        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
+        assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
+
+        user = getObject(response.getLocation(), UserService.class, UserTO.class);
+        assertNotNull(user);
+
+        UserMod userMod = new UserMod();
+        userMod.setPassword("password321");
+
+        response = noContentService.update(user.getKey(), userMod);
+        assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
+        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
+        assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
+
+        response = noContentService.delete(user.getKey());
+        assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
+        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
+        assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
+    }
+
+    @Test
+    public void issueSYNCOPE108() {
+        UserTO userTO = getUniqueSampleTO("syncope108@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getDerAttrs().clear();
+        userTO.getVirAttrs().clear();
+        userTO.getDerAttrs().add(attrTO("csvuserid", null));
+
+        MembershipTO memb12 = new MembershipTO();
+        memb12.setRoleId(12L);
+
+        userTO.getMemberships().add(memb12);
+
+        MembershipTO memb13 = new MembershipTO();
+        memb13.setRoleId(13L);
+
+        userTO.getMemberships().add(memb13);
+
+        userTO.getResources().add(RESOURCE_NAME_CSV);
+
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+        assertEquals(2, actual.getMemberships().size());
+        assertEquals(1, actual.getResources().size());
+
+        ConnObjectTO connObjectTO =
+                resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey());
+        assertNotNull(connObjectTO);
+
+        // -----------------------------------
+        // Remove the first membership: de-provisioning shouldn't happen
+        // -----------------------------------
+        UserMod userMod = new UserMod();
+        userMod.setKey(actual.getKey());
+
+        userMod.getMembershipsToRemove().add(actual.getMemberships().get(0).getKey());
+
+        actual = updateUser(userMod);
+        assertNotNull(actual);
+        assertEquals(1, actual.getMemberships().size());
+
+        connObjectTO = resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey());
+        assertNotNull(connObjectTO);
+        // -----------------------------------
+
+        // -----------------------------------
+        // Remove the resource assigned directly: de-provisioning shouldn't happen
+        // -----------------------------------
+        userMod = new UserMod();
+        userMod.setKey(actual.getKey());
+
+        userMod.getResourcesToRemove().add(actual.getResources().iterator().next());
+
+        actual = updateUser(userMod);
+        assertNotNull(actual);
+        assertEquals(1, actual.getMemberships().size());
+        assertFalse(actual.getResources().isEmpty());
+
+        connObjectTO = resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey());
+        assertNotNull(connObjectTO);
+        // -----------------------------------
+
+        // -----------------------------------
+        // Remove the first membership: de-provisioning should happen
+        // -----------------------------------
+        userMod = new UserMod();
+        userMod.setKey(actual.getKey());
+
+        userMod.getMembershipsToRemove().add(actual.getMemberships().get(0).getKey());
+
+        actual = updateUser(userMod);
+        assertNotNull(actual);
+        assertTrue(actual.getMemberships().isEmpty());
+        assertTrue(actual.getResources().isEmpty());
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey());
+            fail("Read should not succeeed");
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE111() {
+        UserTO userTO = getUniqueSampleTO("syncope111@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getDerAttrs().clear();
+        userTO.getVirAttrs().clear();
+        userTO.getDerAttrs().add(attrTO("csvuserid", null));
+
+        MembershipTO memb12 = new MembershipTO();
+        memb12.setRoleId(12L);
+        memb12.getPlainAttrs().add(attrTO("postalAddress", "postalAddress"));
+        userTO.getMemberships().add(memb12);
+
+        MembershipTO memb13 = new MembershipTO();
+        memb13.setRoleId(13L);
+        userTO.getMemberships().add(memb13);
+
+        userTO.getResources().add(RESOURCE_NAME_LDAP);
+
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+        assertEquals(2, actual.getMemberships().size());
+
+        ConnObjectTO connObjectTO =
+                resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.USER, actual.getKey());
+        assertNotNull(connObjectTO);
+
+        AttrTO postalAddress = connObjectTO.getPlainAttrMap().get("postalAddress");
+        assertNotNull(postalAddress);
+        assertEquals(1, postalAddress.getValues().size());
+        assertEquals("postalAddress", postalAddress.getValues().get(0));
+
+        AttrTO title = connObjectTO.getPlainAttrMap().get("title");
+        assertNotNull(title);
+        assertEquals(2, title.getValues().size());
+        assertTrue(title.getValues().contains("r12") && title.getValues().contains("r13"));
+
+        // -----------------------------------
+        // Remove the first membership and check for membership attr propagation and role attr propagation
+        // -----------------------------------
+        UserMod userMod = new UserMod();
+        userMod.setKey(actual.getKey());
+
+        MembershipTO membershipTO = actual.getMemberships().get(0).getRoleId() == 12L
+                ? actual.getMemberships().get(0)
+                : actual.getMemberships().get(1);
+
+        userMod.getMembershipsToRemove().add(membershipTO.getKey());
+
+        actual = updateUser(userMod);
+        assertNotNull(actual);
+        assertEquals(1, actual.getMemberships().size());
+
+        connObjectTO = resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.USER, actual.getKey());
+        assertNotNull(connObjectTO);
+
+        postalAddress = connObjectTO.getPlainAttrMap().get("postalAddress");
+        assertTrue(postalAddress == null || postalAddress.getValues().isEmpty()
+                || StringUtils.isNotBlank(postalAddress.getValues().get(0)));
+
+        title = connObjectTO.getPlainAttrMap().get("title");
+        assertNotNull(title);
+        assertEquals(1, title.getValues().size());
+        assertTrue(title.getValues().contains("r13"));
+        // -----------------------------------
+    }
+
+    @Test
+    public void issueSYNCOPE185() {
+        // 1. create user with LDAP resource, succesfully propagated
+        UserTO userTO = getSampleTO("syncope185@syncope.apache.org");
+        userTO.getVirAttrs().clear();
+        userTO.getResources().add(RESOURCE_NAME_LDAP);
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+        assertFalse(userTO.getPropagationStatusTOs().isEmpty());
+        assertEquals(RESOURCE_NAME_LDAP, userTO.getPropagationStatusTOs().get(0).getResource());
+        assertEquals(PropagationTaskExecStatus.SUCCESS, userTO.getPropagationStatusTOs().get(0).getStatus());
+
+        // 2. delete this user
+        userService.delete(userTO.getKey());
+
+        // 3. try (and fail) to find this user on the external LDAP resource
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.USER, userTO.getKey());
+            fail("This entry should not be present on this resource");
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+        }
+    }
+
+    @Test()
+    public void issueSYNCOPE51() {
+        AttrTO defaultCA = configurationService.read("password.cipher.algorithm");
+        final String originalCAValue = defaultCA.getValues().get(0);
+        defaultCA.getValues().set(0, "MD5");
+        configurationService.set(defaultCA.getSchema(), defaultCA);
+
+        AttrTO newCA = configurationService.read(defaultCA.getSchema());
+        assertEquals(defaultCA, newCA);
+
+        UserTO userTO = getSampleTO("syncope51@syncope.apache.org");
+        userTO.setPassword("password");
+
+        try {
+            createUser(userTO);
+            fail("Create user should not succeed");
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+            assertTrue(e.getElements().iterator().next().contains("MD5"));
+        }
+
+        defaultCA.getValues().set(0, originalCAValue);
+        configurationService.set(defaultCA.getSchema(), defaultCA);
+
+        AttrTO oldCA = configurationService.read(defaultCA.getSchema());
+        assertEquals(defaultCA, oldCA);
+    }
+
+    @Test
+    public void issueSYNCOPE267() {
+        // ----------------------------------
+        // create user and check virtual attribute value propagation
+        // ----------------------------------
+        UserTO userTO = getUniqueSampleTO("syncope267@apache.org");
+        userTO.getResources().clear();
+        userTO.getResources().add(RESOURCE_NAME_DBVIRATTR);
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+        assertFalse(userTO.getPropagationStatusTOs().isEmpty());
+        assertEquals(RESOURCE_NAME_DBVIRATTR, userTO.getPropagationStatusTOs().get(0).getResource());
+        assertEquals(PropagationTaskExecStatus.SUBMITTED, userTO.getPropagationStatusTOs().get(0).getStatus());
+
+        ConnObjectTO connObjectTO =
+                resourceService.getConnectorObject(RESOURCE_NAME_DBVIRATTR, SubjectType.USER, userTO.getKey());
+        assertNotNull(connObjectTO);
+        assertEquals("virtualvalue", connObjectTO.getPlainAttrMap().get("USERNAME").getValues().get(0));
+        // ----------------------------------
+
+        userTO = userService.read(userTO.getKey());
+
+        assertNotNull(userTO);
+        assertEquals(1, userTO.getVirAttrs().size());
+        assertEquals("virtualvalue", userTO.getVirAttrs().get(0).getValues().get(0));
+    }
+
+    @Test
+    public void issueSYNCOPE266() {
+        UserTO userTO = getUniqueSampleTO("syncope266@apache.org");
+        userTO.getResources().clear();
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+
+        // this resource has not a mapping for Password
+        userMod.getResourcesToAdd().add(RESOURCE_NAME_UPDATE);
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+    }
+
+    @Test
+    public void issueSYNCOPE279() {
+        UserTO userTO = getUniqueSampleTO("syncope279@apache.org");
+        userTO.getResources().clear();
+        userTO.getResources().add(RESOURCE_NAME_TIMEOUT);
+        userTO = createUser(userTO);
+        assertEquals(RESOURCE_NAME_TIMEOUT, userTO.getPropagationStatusTOs().get(0).getResource());
+        assertNotNull(userTO.getPropagationStatusTOs().get(0).getFailureReason());
+        assertEquals(PropagationTaskExecStatus.UNSUBMITTED, userTO.getPropagationStatusTOs().get(0).getStatus());
+    }
+
+    @Test
+    public void issueSYNCOPE122() {
+        // 1. create user on testdb and testdb2
+        UserTO userTO = getUniqueSampleTO("syncope122@apache.org");
+        userTO.getResources().clear();
+
+        userTO.getResources().add(RESOURCE_NAME_TESTDB);
+        userTO.getResources().add(RESOURCE_NAME_TESTDB2);
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+        assertTrue(userTO.getResources().contains(RESOURCE_NAME_TESTDB));
+        assertTrue(userTO.getResources().contains(RESOURCE_NAME_TESTDB2));
+
+        final String pwdOnSyncope = userTO.getPassword();
+
+        ConnObjectTO userOnDb = resourceService.getConnectorObject(
+                RESOURCE_NAME_TESTDB, SubjectType.USER, userTO.getKey());
+        final AttrTO pwdOnTestDbAttr = userOnDb.getPlainAttrMap().get(OperationalAttributes.PASSWORD_NAME);
+        assertNotNull(pwdOnTestDbAttr);
+        assertNotNull(pwdOnTestDbAttr.getValues());
+        assertFalse(pwdOnTestDbAttr.getValues().isEmpty());
+        final String pwdOnTestDb = pwdOnTestDbAttr.getValues().iterator().next();
+
+        ConnObjectTO userOnDb2 = resourceService.getConnectorObject(
+                RESOURCE_NAME_TESTDB2, SubjectType.USER, userTO.getKey());
+        final AttrTO pwdOnTestDb2Attr = userOnDb2.getPlainAttrMap().get(OperationalAttributes.PASSWORD_NAME);
+        assertNotNull(pwdOnTestDb2Attr);
+        assertNotNull(pwdOnTestDb2Attr.getValues());
+        assertFalse(pwdOnTestDb2Attr.getValues().isEmpty());
+        final String pwdOnTestDb2 = pwdOnTestDb2Attr.getValues().iterator().next();
+
+        // 2. request to change password only on testdb (no Syncope, no testdb2)
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.setPassword(getUUIDString());
+        StatusMod pwdPropRequest = new StatusMod();
+        pwdPropRequest.setOnSyncope(false);
+        pwdPropRequest.getResourceNames().add(RESOURCE_NAME_TESTDB);
+        userMod.setPwdPropRequest(pwdPropRequest);
+
+        userTO = updateUser(userMod);
+
+        // 3a. Chech that only a single propagation took place
+        assertNotNull(userTO.getPropagationStatusTOs());
+        assertEquals(1, userTO.getPropagationStatusTOs().size());
+        assertEquals(RESOURCE_NAME_TESTDB, userTO.getPropagationStatusTOs().iterator().next().getResource());
+
+        // 3b. verify that password hasn't changed on Syncope
+        assertEquals(pwdOnSyncope, userTO.getPassword());
+
+        // 3c. verify that password *has* changed on testdb
+        userOnDb = resourceService.getConnectorObject(RESOURCE_NAME_TESTDB, SubjectType.USER, userTO.getKey());
+        final AttrTO pwdOnTestDbAttrAfter = userOnDb.getPlainAttrMap().get(OperationalAttributes.PASSWORD_NAME);
+        assertNotNull(pwdOnTestDbAttrAfter);
+        assertNotNull(pwdOnTestDbAttrAfter.getValues());
+        assertFalse(pwdOnTestDbAttrAfter.getValues().isEmpty());
+        assertNotEquals(pwdOnTestDb, pwdOnTestDbAttrAfter.getValues().iterator().next());
+
+        // 3d. verify that password hasn't changed on testdb2
+        userOnDb2 = resourceService.getConnectorObject(RESOURCE_NAME_TESTDB2, SubjectType.USER, userTO.getKey());
+        final AttrTO pwdOnTestDb2AttrAfter = userOnDb2.getPlainAttrMap().get(OperationalAttributes.PASSWORD_NAME);
+        assertNotNull(pwdOnTestDb2AttrAfter);
+        assertNotNull(pwdOnTestDb2AttrAfter.getValues());
+        assertFalse(pwdOnTestDb2AttrAfter.getValues().isEmpty());
+        assertEquals(pwdOnTestDb2, pwdOnTestDb2AttrAfter.getValues().iterator().next());
+    }
+
+    @Test
+    public void isseSYNCOPE136AES() {
+        // 1. read configured cipher algorithm in order to be able to restore it at the end of test
+        AttrTO pwdCipherAlgo = configurationService.read("password.cipher.algorithm");
+        final String origpwdCipherAlgo = pwdCipherAlgo.getValues().get(0);
+
+        // 2. set AES password cipher algorithm
+        pwdCipherAlgo.getValues().set(0, "AES");
+        configurationService.set(pwdCipherAlgo.getSchema(), pwdCipherAlgo);
+
+        // 3. create user with no resources
+        UserTO userTO = getUniqueSampleTO("syncope136_AES@apache.org");
+        userTO.getResources().clear();
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        // 4. update user, assign a propagation primary resource but don't provide any password
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.getResourcesToAdd().add(RESOURCE_NAME_WS1);
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+
+        // 5. verify that propagation was successful
+        List<PropagationStatus> props = userTO.getPropagationStatusTOs();
+        assertNotNull(props);
+        assertEquals(1, props.size());
+        PropagationStatus prop = props.iterator().next();
+        assertNotNull(prop);
+        assertEquals(RESOURCE_NAME_WS1, prop.getResource());
+        assertEquals(PropagationTaskExecStatus.SUBMITTED, prop.getStatus());
+
+        // 6. restore initial cipher algorithm
+        pwdCipherAlgo.getValues().set(0, origpwdCipherAlgo);
+        configurationService.set(pwdCipherAlgo.getSchema(), pwdCipherAlgo);
+    }
+
+    @Test
+    public void isseSYNCOPE136Random() {
+        // 1. create user with no resources
+        UserTO userTO = getUniqueSampleTO("syncope136_Random@apache.org");
+        userTO.getResources().clear();
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        // 2. update user, assign a propagation primary resource but don't provide any password
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.getResourcesToAdd().add(RESOURCE_NAME_LDAP);
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+
+        // 3. verify that propagation was successful
+        List<PropagationStatus> props = userTO.getPropagationStatusTOs();
+        assertNotNull(props);
+        assertEquals(1, props.size());
+        PropagationStatus prop = props.iterator().next();
+        assertNotNull(prop);
+        assertEquals(RESOURCE_NAME_LDAP, prop.getResource());
+        assertEquals(PropagationTaskExecStatus.SUCCESS, prop.getStatus());
+    }
+
+    @Test
+    public void mappingPurpose() {
+        UserTO userTO = getUniqueSampleTO("mpurpose@apache.org");
+
+        AttrTO csvuserid = new AttrTO();
+        csvuserid.setSchema("csvuserid");
+        userTO.getDerAttrs().add(csvuserid);
+
+        userTO.getResources().clear();
+        userTO.getResources().add(RESOURCE_NAME_CSV);
+
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+
+        ConnObjectTO connObjectTO =
+                resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey());
+        assertNull(connObjectTO.getPlainAttrMap().get("email"));
+    }
+
+    @Test
+    public void issueSYNCOPE265() {
+        for (long i = 1; i <= 5; i++) {
+            UserMod userMod = new UserMod();
+            userMod.setKey(i);
+
+            AttrMod attributeMod = new AttrMod();
+            attributeMod.setSchema("type");
+            attributeMod.getValuesToBeAdded().add("a type");
+
+            userMod.getPlainAttrsToRemove().add("type");
+            userMod.getPlainAttrsToUpdate().add(attributeMod);
+
+            UserTO userTO = updateUser(userMod);
+            assertEquals("a type", userTO.getPlainAttrMap().get("type").getValues().get(0));
+        }
+    }
+
+    @Test
+    public void bulkActions() {
+        final BulkAction bulkAction = new BulkAction();
+
+        for (int i = 0; i < 10; i++) {
+            UserTO userTO = getUniqueSampleTO("bulk_" + i + "@apache.org");
+            bulkAction.getTargets().add(String.valueOf(createUser(userTO).getKey()));
+        }
+
+        // check for a fail
+        bulkAction.getTargets().add(String.valueOf(Long.MAX_VALUE));
+
+        assertEquals(11, bulkAction.getTargets().size());
+
+        bulkAction.setOperation(BulkAction.Type.SUSPEND);
+        BulkActionResult res = userService.bulk(bulkAction);
+        assertEquals(10, res.getResultByStatus(Status.SUCCESS).size());
+        assertEquals(1, res.getResultByStatus(Status.FAILURE).size());
+        assertEquals("suspended", userService.read(
+                Long.parseLong(res.getResultByStatus(Status.SUCCESS).get(3).toString())).getStatus());
+
+        bulkAction.setOperation(BulkAction.Type.REACTIVATE);
+        res = userService.bulk(bulkAction);
+        assertEquals(10, res.getResultByStatus(Status.SUCCESS).size());
+        assertEquals(1, res.getResultByStatus(Status.FAILURE).size());
+        assertEquals("active", userService.read(
+                Long.parseLong(res.getResultByStatus(Status.SUCCESS).get(3).toString())).getStatus());
+
+        bulkAction.setOperation(BulkAction.Type.DELETE);
+        res = userService.bulk(bulkAction);
+        assertEquals(10, res.getResultByStatus(Status.SUCCESS).size());
+        assertEquals(1, res.getResultByStatus(Status.FAILURE).size());
+    }
+
+    @Test
+    public void issueSYNCOPE354() {
+        // change resource-ldap role mapping for including uniqueMember (need for assertions below)
+        ResourceTO ldap = resourceService.read(RESOURCE_NAME_LDAP);
+        for (MappingItemTO item : ldap.getRmapping().getItems()) {
+            if ("description".equals(item.getExtAttrName())) {
+                item.setExtAttrName("uniqueMember");
+            }
+        }
+        resourceService.update(ldap.getKey(), ldap);
+
+        // 1. create role with LDAP resource
+        RoleTO roleTO = new RoleTO();
+        roleTO.setName("SYNCOPE354-" + getUUIDString());
+        roleTO.setParent(8L);
+        roleTO.getResources().add(RESOURCE_NAME_LDAP);
+
+        roleTO = createRole(roleTO);
+        assertNotNull(roleTO);
+
+        // 2. create user with LDAP resource and membership of the above role
+        UserTO userTO = getUniqueSampleTO("syncope354@syncope.apache.org");
+        userTO.getResources().add(RESOURCE_NAME_LDAP);
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(roleTO.getKey());
+        userTO.getMemberships().add(membershipTO);
+
+        userTO = createUser(userTO);
+        assertTrue(userTO.getResources().contains(RESOURCE_NAME_LDAP));
+
+        // 3. read role on resource, check that user DN is included in uniqueMember
+        ConnObjectTO connObj = resourceService.getConnectorObject(
+                RESOURCE_NAME_LDAP, SubjectType.ROLE, roleTO.getKey());
+        assertNotNull(connObj);
+        assertTrue(connObj.getPlainAttrMap().get("uniqueMember").getValues().
+                contains("uid=" + userTO.getUsername() + ",ou=people,o=isp"));
+
+        // 4. remove membership
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.getMembershipsToRemove().add(userTO.getMemberships().iterator().next().getKey());
+
+        userTO = updateUser(userMod);
+        assertTrue(userTO.getResources().contains(RESOURCE_NAME_LDAP));
+
+        // 5. read role on resource, check that user DN was removed from uniqueMember
+        connObj = resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, roleTO.getKey());
+        assertNotNull(connObj);
+        assertFalse(connObj.getPlainAttrMap().get("uniqueMember").getValues().
+                contains("uid=" + userTO.getUsername() + ",ou=people,o=isp"));
+
+        // 6. restore original resource-ldap role mapping
+        for (MappingItemTO item : ldap.getRmapping().getItems()) {
+            if ("uniqueMember".equals(item.getExtAttrName())) {
+                item.setExtAttrName("description");
+            }
+        }
+        resourceService.update(ldap.getKey(), ldap);
+    }
+
+    @Test
+    public void issueSYNCOPE357() throws IOException {
+        // 1. create role with LDAP resource
+        RoleTO roleTO = new RoleTO();
+        roleTO.setName("SYNCOPE357-" + getUUIDString());
+        roleTO.setParent(8L);
+        roleTO.getResources().add(RESOURCE_NAME_LDAP);
+
+        roleTO = createRole(roleTO);
+        assertNotNull(roleTO);
+
+        // 2. create user with membership of the above role
+        UserTO userTO = getUniqueSampleTO("syncope357@syncope.apache.org");
+        userTO.getPlainAttrs().add(attrTO("obscure", "valueToBeObscured"));
+        userTO.getPlainAttrs().add(attrTO("photo",
+                Base64Utility.encode(IOUtils.readBytesFromStream(getClass().getResourceAsStream("/favicon.jpg")))));
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(roleTO.getKey());
+        userTO.getMemberships().add(membershipTO);
+
+        userTO = createUser(userTO);
+        assertTrue(userTO.getResources().contains(RESOURCE_NAME_LDAP));
+        assertNotNull(userTO.getPlainAttrMap().get("obscure"));
+        assertNotNull(userTO.getPlainAttrMap().get("photo"));
+
+        // 3. read user on resource
+        ConnObjectTO connObj = resourceService.getConnectorObject(
+                RESOURCE_NAME_LDAP, SubjectType.USER, userTO.getKey());
+        assertNotNull(connObj);
+        AttrTO registeredAddress = connObj.getPlainAttrMap().get("registeredAddress");
+        assertNotNull(registeredAddress);
+        assertEquals(userTO.getPlainAttrMap().get("obscure").getValues(), registeredAddress.getValues());
+        AttrTO jpegPhoto = connObj.getPlainAttrMap().get("jpegPhoto");
+        assertNotNull(jpegPhoto);
+        assertEquals(userTO.getPlainAttrMap().get("photo").getValues(), jpegPhoto.getValues());
+
+        // 4. remove role
+        roleService.delete(roleTO.getKey());
+
+        // 5. try to read user on resource: fail
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.USER, userTO.getKey());
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE383() {
+        // 1. create user without resources
+        UserTO userTO = getUniqueSampleTO("syncope383@apache.org");
+        userTO.getResources().clear();
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        // 2. assign resource without specifying a new pwd and check propagation failure
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.getResourcesToAdd().add(RESOURCE_NAME_TESTDB);
+        userTO = updateUser(userMod);
+        assertEquals(RESOURCE_NAME_TESTDB, userTO.getResources().iterator().next());
+        assertFalse(userTO.getPropagationStatusTOs().get(0).getStatus().isSuccessful());
+        assertNotNull(userTO.getPropagationStatusTOs().get(0).getFailureReason());
+
+        // 3. request to change password only on testdb
+        userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.setPassword(getUUIDString());
+        StatusMod pwdPropRequest = new StatusMod();
+        pwdPropRequest.getResourceNames().add(RESOURCE_NAME_TESTDB);
+        userMod.setPwdPropRequest(pwdPropRequest);
+
+        userTO = updateUser(userMod);
+        assertEquals(RESOURCE_NAME_TESTDB, userTO.getResources().iterator().next());
+        assertTrue(userTO.getPropagationStatusTOs().get(0).getStatus().isSuccessful());
+    }
+
+    @Test
+    public void issueSYNCOPE402() {
+        // 1. create an user with strict mandatory attributes only
+        UserTO userTO = new UserTO();
+        String userId = getUUIDString() + "syncope402@syncope.apache.org";
+        userTO.setUsername(userId);
+        userTO.setPassword("password");
+
+        userTO.getPlainAttrs().add(attrTO("userId", userId));
+        userTO.getPlainAttrs().add(attrTO("fullname", userId));
+        userTO.getPlainAttrs().add(attrTO("surname", userId));
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+        assertTrue(userTO.getResources().isEmpty());
+
+        //2. update assigning a resource NOT forcing mandatory constraints
+        // AND primary: must fail with PropagationException
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.setPassword("newPassword");
+
+        userMod.getResourcesToAdd().add(RESOURCE_NAME_WS1);
+        userMod.getResourcesToAdd().add(RESOURCE_NAME_TESTDB);
+        userTO = updateUser(userMod);
+
+        List<PropagationStatus> propagationStatuses = userTO.getPropagationStatusTOs();
+        PropagationStatus ws1PropagationStatus = null;
+        if (propagationStatuses != null) {
+            for (PropagationStatus propStatus : propagationStatuses) {
+                if (RESOURCE_NAME_WS1.equals(propStatus.getResource())) {
+                    ws1PropagationStatus = propStatus;
+                    break;
+                }
+            }
+        }
+        assertNotNull(ws1PropagationStatus);
+        assertEquals(RESOURCE_NAME_WS1, ws1PropagationStatus.getResource());
+        assertNotNull(ws1PropagationStatus.getFailureReason());
+        assertEquals(PropagationTaskExecStatus.UNSUBMITTED, ws1PropagationStatus.getStatus());
+    }
+
+    @Test
+    public void unlink() {
+        UserTO userTO = getUniqueSampleTO("unlink@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getDerAttrs().clear();
+        userTO.getVirAttrs().clear();
+        userTO.getDerAttrs().add(attrTO("csvuserid", null));
+        userTO.getResources().add(RESOURCE_NAME_CSV);
+
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey()));
+
+        assertNotNull(userService.bulkDeassociation(actual.getKey(),
+                ResourceDeassociationActionType.UNLINK,
+                CollectionWrapper.wrap(RESOURCE_NAME_CSV, ResourceName.class)).
+                readEntity(BulkActionResult.class));
+
+        actual = userService.read(actual.getKey());
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+
+        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey()));
+    }
+
+    @Test
+    public void link() {
+        UserTO userTO = getUniqueSampleTO("link@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getDerAttrs().clear();
+        userTO.getVirAttrs().clear();
+        userTO.getDerAttrs().add(attrTO("csvuserid", null));
+
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey());
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+
+        final ResourceAssociationMod associationMod = new ResourceAssociationMod();
+        associationMod.getTargetResources().addAll(CollectionWrapper.wrap(RESOURCE_NAME_CSV, ResourceName.class));
+
+        assertNotNull(userService.bulkAssociation(
+                actual.getKey(), ResourceAssociationActionType.LINK, associationMod).readEntity(BulkActionResult.class));
+
+        actual = userService.read(actual.getKey());
+        assertNotNull(actual);
+        assertFalse(actual.getResources().isEmpty());
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey());
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+    }
+
+    @Test
+    public void unassign() {
+        UserTO userTO = getUniqueSampleTO("unassign@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getDerAttrs().clear();
+        userTO.getVirAttrs().clear();
+        userTO.getDerAttrs().add(attrTO("csvuserid", null));
+        userTO.getResources().add(RESOURCE_NAME_CSV);
+
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey()));
+
+        assertNotNull(userService.bulkDeassociation(actual.getKey(),
+                ResourceDeassociationActionType.UNASSIGN,
+                CollectionWrapper.wrap(RESOURCE_NAME_CSV, ResourceName.class)).
+                readEntity(BulkActionResult.class));
+
+        actual = userService.read(actual.getKey());
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey());
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+    }
+
+    @Test
+    public void assign() {
+        UserTO userTO = getUniqueSampleTO("assign@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getDerAttrs().clear();
+        userTO.getVirAttrs().clear();
+        userTO.getDerAttrs().add(attrTO("csvuserid", null));
+
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey());
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+
+        final ResourceAssociationMod associationMod = new ResourceAssociationMod();
+        associationMod.getTargetResources().addAll(CollectionWrapper.wrap(RESOURCE_NAME_CSV, ResourceName.class));
+        associationMod.setChangePwd(true);
+        associationMod.setPassword("password");
+
+        assertNotNull(userService.bulkAssociation(actual.getKey(), ResourceAssociationActionType.ASSIGN, associationMod)
+                .readEntity(BulkActionResult.class));
+
+        actual = userService.read(actual.getKey());
+        assertNotNull(actual);
+        assertFalse(actual.getResources().isEmpty());
+        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey()));
+    }
+
+    @Test
+    public void deprovision() {
+        UserTO userTO = getUniqueSampleTO("deprovision@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getDerAttrs().clear();
+        userTO.getVirAttrs().clear();
+        userTO.getDerAttrs().add(attrTO("csvuserid", null));
+        userTO.getResources().add(RESOURCE_NAME_CSV);
+
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey()));
+
+        assertNotNull(userService.bulkDeassociation(actual.getKey(),
+                ResourceDeassociationActionType.DEPROVISION,
+                CollectionWrapper.wrap(RESOURCE_NAME_CSV, ResourceName.class)).
+                readEntity(BulkActionResult.class));
+
+        actual = userService.read(actual.getKey());
+        assertNotNull(actual);
+        assertFalse(actual.getResources().isEmpty());
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey());
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+    }
+
+    @Test
+    public void provision() {
+        UserTO userTO = getUniqueSampleTO("provision@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getDerAttrs().clear();
+        userTO.getVirAttrs().clear();
+        userTO.getDerAttrs().add(attrTO("csvuserid", null));
+
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey());
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+
+        final ResourceAssociationMod associationMod = new ResourceAssociationMod();
+        associationMod.getTargetResources().addAll(CollectionWrapper.wrap(RESOURCE_NAME_CSV, ResourceName.class));
+        associationMod.setChangePwd(true);
+        associationMod.setPassword("password");
+
+        assertNotNull(userService.bulkAssociation(actual.getKey(), ResourceAssociationActionType.PROVISION,
+                associationMod)
+                .readEntity(BulkActionResult.class));
+
+        actual = userService.read(actual.getKey());
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey()));
+    }
+
+    @Test
+    public void deprovisionUnlinked() {
+        UserTO userTO = getUniqueSampleTO("provision@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getDerAttrs().clear();
+        userTO.getVirAttrs().clear();
+        userTO.getDerAttrs().add(attrTO("csvuserid", null));
+
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey());
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+
+        final ResourceAssociationMod associationMod = new ResourceAssociationMod();
+        associationMod.getTargetResources().addAll(CollectionWrapper.wrap(RESOURCE_NAME_CSV, ResourceName.class));
+        associationMod.setChangePwd(true);
+        associationMod.setPassword("password");
+
+        assertNotNull(userService.bulkAssociation(actual.getKey(), ResourceAssociationActionType.PROVISION,
+                associationMod)
+                .readEntity(BulkActionResult.class));
+
+        actual = userService.read(actual.getKey());
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey()));
+
+        assertNotNull(userService.bulkDeassociation(actual.getKey(),
+                ResourceDeassociationActionType.DEPROVISION,
+                CollectionWrapper.wrap(RESOURCE_NAME_CSV, ResourceName.class)).
+                readEntity(BulkActionResult.class));
+
+        actual = userService.read(actual.getKey());
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_CSV, SubjectType.USER, actual.getKey());
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE420() {
+        UserTO userTO = getUniqueSampleTO("syncope420@syncope.apache.org");
+        userTO.getPlainAttrs().add(attrTO("makeItDouble", "3"));
+
+        userTO = createUser(userTO);
+        assertEquals("6", userTO.getPlainAttrMap().get("makeItDouble").getValues().get(0));
+
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.getPlainAttrsToRemove().add("makeItDouble");
+        userMod.getPlainAttrsToUpdate().add(attrMod("makeItDouble", "7"));
+
+        userTO = updateUser(userMod);
+        assertEquals("14", userTO.getPlainAttrMap().get("makeItDouble").getValues().get(0));
+    }
+
+    @Test
+    public void issueSYNCOPE426() {
+        UserTO userTO = getUniqueSampleTO("syncope426@syncope.apache.org");
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        UserMod userMod = new UserMod();
+        userMod.setPassword("anotherPassword123");
+        userTO = userService.update(userTO.getKey(), userMod).readEntity(UserTO.class);
+        assertNotNull(userTO);
+    }
+
+    @Test
+    public void issueSYNCOPE435() {
+        // 1. create user without password
+        UserTO userTO = getUniqueSampleTO("syncope435@syncope.apache.org");
+        userTO.setPassword(null);
+        userTO = createUser(userTO, false);
+        assertNotNull(userTO);
+
+        // 2. try to update user by subscribing a resource - works but propagation is not even attempted
+        UserMod userMod = new UserMod();
+        userMod.getResourcesToAdd().add(RESOURCE_NAME_WS1);
+
+        userTO = userService.update(userTO.getKey(), userMod).readEntity(UserTO.class);
+        assertEquals(Collections.singleton(RESOURCE_NAME_WS1), userTO.getResources());
+        assertFalse(userTO.getPropagationStatusTOs().get(0).getStatus().isSuccessful());
+        assertTrue(userTO.getPropagationStatusTOs().get(0).getFailureReason().
+                startsWith("Not attempted because there are mandatory attributes without value(s): [__PASSWORD__]"));
+    }
+
+    @Test
+    public void ifMatch() {
+        UserTO userTO = userService.create(getUniqueSampleTO("ifmatch@syncope.apache.org"), true).
+                readEntity(UserTO.class);
+        assertNotNull(userTO);
+        assertNotNull(userTO.getKey());
+
+        EntityTag etag = adminClient.getLatestEntityTag(userService);
+        assertNotNull(etag);
+        assertTrue(StringUtils.isNotBlank(etag.getValue()));
+
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.setUsername(userTO.getUsername() + "XX");
+        userTO = userService.update(userMod.getKey(), userMod).readEntity(UserTO.class);
+        assertTrue(userTO.getUsername().endsWith("XX"));
+        EntityTag etag1 = adminClient.getLatestEntityTag(userService);
+        assertFalse(etag.getValue().equals(etag1.getValue()));
+
+        UserService ifMatchService = adminClient.ifMatch(UserService.class, etag);
+        userMod.setUsername(userTO.getUsername() + "YY");
+        try {
+            ifMatchService.update(userMod.getKey(), userMod);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.ConcurrentModification, e.getType());
+        }
+
+        userTO = userService.read(userTO.getKey());
+        assertTrue(userTO.getUsername().endsWith("XX"));
+    }
+
+    @Test
+    public void issueSYNCOPE454() throws NamingException {
+        // 1. create user with LDAP resource (with 'Generate password if missing' enabled)
+        UserTO userTO = getUniqueSampleTO("syncope454@syncope.apache.org");
+        userTO.getResources().add(RESOURCE_NAME_LDAP);
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        // 2. read resource configuration for LDAP binding
+        ConnObjectTO connObject =
+                resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.USER, userTO.getKey());
+
+        // 3. try (and succeed) to perform simple LDAP binding with provided password ('password123')
+        assertNotNull(getLdapRemoteObject(
+                connObject.getPlainAttrMap().get(Name.NAME).getValues().get(0),
+                "password123",
+                connObject.getPlainAttrMap().get(Name.NAME).getValues().get(0)));
+
+        // 4. update user without any password change request
+        UserMod userMod = new UserMod();
+        userMod.setKey(userTO.getKey());
+        userMod.setPwdPropRequest(new StatusMod());
+        userMod.getPlainAttrsToUpdate().add(attrMod("surname", "surname2"));
+
+        userService.update(userTO.getKey(), userMod);
+
+        // 5. try (and succeed again) to perform simple LDAP binding: password has not changed
+        assertNotNull(getLdapRemoteObject(
+                connObject.getPlainAttrMap().get(Name.NAME).getValues().get(0),
+                "password123",
+                connObject.getPlainAttrMap().get(Name.NAME).getValues().get(0)));
+    }
+
+    @Test
+    public void issueSYNCOPE493() {
+        // 1.  create user and check that firstname is not propagated on resource with mapping for firstname set to NONE
+        UserTO userTO = getUniqueSampleTO("issueSYNCOPE493@test.org");
+        userTO.getResources().add(RESOURCE_NAME_WS1);
+        us

<TRUNCATED>

[12/15] syncope git commit: FIT server integration tests

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/PlainSchemaITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/PlainSchemaITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/PlainSchemaITCase.java
new file mode 100644
index 0000000..448d1fa
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/PlainSchemaITCase.java
@@ -0,0 +1,317 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+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.List;
+import javax.ws.rs.core.Response;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.syncope.common.lib.AttributableOperations;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.PlainSchemaTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.CipherAlgorithm;
+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.service.SchemaService;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class PlainSchemaITCase extends AbstractITCase {
+
+    private PlainSchemaTO buildPlainSchemaTO(final String name, final AttrSchemaType type) {
+        PlainSchemaTO schemaTO = new PlainSchemaTO();
+        schemaTO.setKey(name + getUUIDString());
+        schemaTO.setType(type);
+        return schemaTO;
+    }
+
+    @Test
+    public void create() {
+        PlainSchemaTO schemaTO = buildPlainSchemaTO("testAttribute", AttrSchemaType.String);
+        schemaTO.setMandatoryCondition("false");
+
+        PlainSchemaTO newPlainSchemaTO = createSchema(AttributableType.USER, SchemaType.PLAIN, schemaTO);
+        assertEquals(schemaTO, newPlainSchemaTO);
+
+        newPlainSchemaTO = createSchema(AttributableType.MEMBERSHIP, SchemaType.PLAIN, schemaTO);
+        assertEquals(schemaTO, newPlainSchemaTO);
+    }
+
+    @Test
+    public void createWithNotPermittedName() {
+        PlainSchemaTO schemaTO = new PlainSchemaTO();
+        schemaTO.setKey("failedLogins");
+        schemaTO.setType(AttrSchemaType.String);
+
+        try {
+            createSchema(AttributableType.USER, SchemaType.PLAIN, schemaTO);
+            fail("This should not be reacheable");
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidPlainSchema, e.getType());
+
+            assertTrue(e.getElements().iterator().next().toString().
+                    contains(EntityViolationType.InvalidName.name()));
+        }
+    }
+
+    @Test
+    public void createREnumWithoutEnumeration() {
+        PlainSchemaTO schemaTO = new PlainSchemaTO();
+        schemaTO.setKey("enumcheck");
+        schemaTO.setType(AttrSchemaType.Enum);
+
+        try {
+            createSchema(AttributableType.ROLE, SchemaType.PLAIN, schemaTO);
+            fail("This should not be reacheable");
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidPlainSchema, e.getType());
+
+            assertTrue(e.getElements().iterator().next().toString().
+                    contains(EntityViolationType.InvalidSchemaEnum.name()));
+        }
+    }
+
+    @Test
+    public void createUEnumWithoutEnumeration() {
+        PlainSchemaTO schemaTO = new PlainSchemaTO();
+        schemaTO.setKey("enumcheck");
+        schemaTO.setType(AttrSchemaType.Enum);
+
+        try {
+            createSchema(AttributableType.USER, SchemaType.PLAIN, schemaTO);
+            fail("This should not be reacheable");
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidPlainSchema, e.getType());
+
+            assertTrue(e.getElements().iterator().next().contains(EntityViolationType.InvalidSchemaEnum.name()));
+        }
+    }
+
+    @Test
+    public void createEncrypted() {
+        PlainSchemaTO schemaTO = new PlainSchemaTO();
+        schemaTO.setKey("encrypted");
+        schemaTO.setType(AttrSchemaType.Encrypted);
+        schemaTO.setCipherAlgorithm(CipherAlgorithm.AES);
+        schemaTO.setSecretKey("huhadfhsjfsfsdkj!####");
+
+        createSchema(AttributableType.MEMBERSHIP, SchemaType.PLAIN, schemaTO);
+    }
+
+    @Test
+    public void createBinary() {
+        PlainSchemaTO schemaTO = new PlainSchemaTO();
+        schemaTO.setKey("x509certificate");
+        schemaTO.setType(AttrSchemaType.Binary);
+        schemaTO.setMimeType("application/x-x509-ca-cert");
+
+        createSchema(AttributableType.ROLE, SchemaType.PLAIN, schemaTO);
+    }
+
+    @Test
+    public void delete() {
+        PlainSchemaTO schemaTO = buildPlainSchemaTO("todelete", AttrSchemaType.String);
+        schemaTO.setMandatoryCondition("false");
+        createSchema(AttributableType.USER, SchemaType.PLAIN, schemaTO);
+
+        schemaService.delete(AttributableType.USER, SchemaType.PLAIN, schemaTO.getKey());
+        PlainSchemaTO firstname = null;
+        try {
+            firstname = schemaService.read(AttributableType.USER, SchemaType.PLAIN, schemaTO.getKey());
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
+        }
+        assertNull(firstname);
+    }
+
+    @Test
+    public void list() {
+        List<PlainSchemaTO> userSchemas = schemaService.list(AttributableType.USER, SchemaType.PLAIN);
+        assertFalse(userSchemas.isEmpty());
+        for (PlainSchemaTO schemaTO : userSchemas) {
+            assertNotNull(schemaTO);
+        }
+
+        List<PlainSchemaTO> roleSchemas = schemaService.list(AttributableType.ROLE, SchemaType.PLAIN);
+        assertFalse(roleSchemas.isEmpty());
+        for (PlainSchemaTO schemaTO : roleSchemas) {
+            assertNotNull(schemaTO);
+        }
+
+        List<PlainSchemaTO> membershipSchemas = schemaService.list(AttributableType.MEMBERSHIP, SchemaType.PLAIN);
+        assertFalse(membershipSchemas.isEmpty());
+        for (PlainSchemaTO schemaTO : membershipSchemas) {
+            assertNotNull(schemaTO);
+        }
+    }
+
+    @Test
+    public void update() {
+        PlainSchemaTO schemaTO = schemaService.read(AttributableType.ROLE, SchemaType.PLAIN, "icon");
+        assertNotNull(schemaTO);
+
+        schemaService.update(AttributableType.ROLE, SchemaType.PLAIN, schemaTO.getKey(), schemaTO);
+        PlainSchemaTO updatedTO = schemaService.read(AttributableType.ROLE, SchemaType.PLAIN, "icon");
+        assertEquals(schemaTO, updatedTO);
+
+        updatedTO.setType(AttrSchemaType.Date);
+        try {
+            schemaService.update(AttributableType.ROLE, SchemaType.PLAIN, schemaTO.getKey(), updatedTO);
+            fail("This should not be reacheable");
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidPlainSchema, e.getType());
+        }
+    }
+
+    @Test
+    public void issue258() {
+        PlainSchemaTO schemaTO = new PlainSchemaTO();
+        schemaTO.setKey("schema_issue258");
+        schemaTO.setType(AttrSchemaType.Double);
+
+        schemaTO = createSchema(AttributableType.USER, SchemaType.PLAIN, schemaTO);
+        assertNotNull(schemaTO);
+
+        UserTO userTO = UserITCase.getUniqueSampleTO("issue258@syncope.apache.org");
+        userTO.getPlainAttrs().add(attrTO(schemaTO.getKey(), "1.2"));
+
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        schemaTO.setType(AttrSchemaType.Long);
+        try {
+            schemaService.update(AttributableType.USER, SchemaType.PLAIN, schemaTO.getKey(), schemaTO);
+            fail("This should not be reacheable");
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidPlainSchema, e.getType());
+        }
+    }
+
+    @Test
+    public void issue259() {
+        PlainSchemaTO schemaTO = buildPlainSchemaTO("schema_issue259", AttrSchemaType.Double);
+        schemaTO.setUniqueConstraint(true);
+
+        schemaTO = createSchema(AttributableType.USER, SchemaType.PLAIN, schemaTO);
+        assertNotNull(schemaTO);
+
+        UserTO userTO = UserITCase.getUniqueSampleTO("issue259@syncope.apache.org");
+        userTO.getPlainAttrs().add(attrTO(schemaTO.getKey(), "1"));
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        UserTO newUserTO = SerializationUtils.clone(userTO);
+        MembershipTO membership = new MembershipTO();
+        membership.setRoleId(2L);
+        newUserTO.getMemberships().add(membership);
+
+        UserMod userMod = AttributableOperations.diff(newUserTO, userTO);
+
+        userTO = userService.update(userMod.getKey(), userMod).readEntity(UserTO.class);
+        assertNotNull(userTO);
+    }
+
+    @Test
+    public void issue260() {
+        PlainSchemaTO schemaTO = buildPlainSchemaTO("schema_issue260", AttrSchemaType.Double);
+        schemaTO.setUniqueConstraint(true);
+
+        schemaTO = createSchema(AttributableType.USER, SchemaType.PLAIN, schemaTO);
+        assertNotNull(schemaTO);
+
+        UserTO userTO = UserITCase.getUniqueSampleTO("issue260@syncope.apache.org");
+        userTO.getPlainAttrs().add(attrTO(schemaTO.getKey(), "1.2"));
+        userTO = createUser(userTO);
+        assertNotNull(userTO);
+
+        schemaTO.setUniqueConstraint(false);
+        try {
+            schemaService.update(AttributableType.USER, SchemaType.PLAIN, schemaTO.getKey(), schemaTO);
+            fail("This should not be reacheable");
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidPlainSchema, e.getType());
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE323() {
+        PlainSchemaTO actual = schemaService.read(AttributableType.ROLE, SchemaType.PLAIN, "icon");
+        assertNotNull(actual);
+
+        try {
+            createSchema(AttributableType.ROLE, SchemaType.PLAIN, actual);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.CONFLICT, e.getType().getResponseStatus());
+            assertEquals(ClientExceptionType.EntityExists, e.getType());
+        }
+
+        actual.setKey(null);
+        try {
+            createSchema(AttributableType.ROLE, SchemaType.PLAIN, actual);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.BAD_REQUEST, e.getType().getResponseStatus());
+            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE418() {
+        PlainSchemaTO schema = buildPlainSchemaTO("http://schemas.examples.org/security/authorization/organizationUnit",
+                AttrSchemaType.Double);
+
+        try {
+            createSchema(AttributableType.ROLE, SchemaType.PLAIN, schema);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidPlainSchema, e.getType());
+            assertTrue(e.getElements().iterator().next().contains(EntityViolationType.InvalidName.name()));
+        }
+    }
+
+    @Test
+    public void anonymous() {
+        SchemaService unauthenticated = clientFactory.createAnonymous().getService(SchemaService.class);
+        try {
+            unauthenticated.list(AttributableType.USER, SchemaType.VIRTUAL);
+            fail();
+        } catch (AccessControlException e) {
+            assertNotNull(e);
+        }
+
+        SchemaService anonymous = clientFactory.create(ANONYMOUS_UNAME, ANONYMOUS_KEY).getService(SchemaService.class);
+        assertFalse(anonymous.list(AttributableType.USER, SchemaType.VIRTUAL).isEmpty());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/PolicyITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/PolicyITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/PolicyITCase.java
new file mode 100644
index 0000000..075cdd9
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/PolicyITCase.java
@@ -0,0 +1,238 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.List;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.AccountPolicyTO;
+import org.apache.syncope.common.lib.to.PasswordPolicyTO;
+import org.apache.syncope.common.lib.to.SyncPolicyTO;
+import org.apache.syncope.common.lib.types.AccountPolicySpec;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.PasswordPolicySpec;
+import org.apache.syncope.common.lib.types.PolicyType;
+import org.apache.syncope.common.lib.types.SyncPolicySpec;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class PolicyITCase extends AbstractITCase {
+
+    private SyncPolicyTO buildSyncPolicyTO() {
+        SyncPolicyTO policy = new SyncPolicyTO();
+
+        SyncPolicySpec spec = new SyncPolicySpec();
+        spec.setUserJavaRule(TestSyncRule.class.getName());
+
+        policy.setSpecification(spec);
+        policy.setDescription("Sync policy");
+
+        return policy;
+    }
+
+    @Test
+    public void listByType() {
+        List<SyncPolicyTO> policyTOs = policyService.list(PolicyType.SYNC);
+
+        assertNotNull(policyTOs);
+        assertFalse(policyTOs.isEmpty());
+    }
+
+    @Test
+    public void getAccountPolicy() {
+        AccountPolicyTO policyTO = policyService.read(6L);
+
+        assertNotNull(policyTO);
+        assertTrue(policyTO.getUsedByResources().isEmpty());
+        assertEquals(Arrays.asList(6L, 7L, 10L, 14L), policyTO.getUsedByRoles());
+    }
+
+    @Test
+    public void getPasswordPolicy() {
+        PasswordPolicyTO policyTO = policyService.read(4L);
+
+        assertNotNull(policyTO);
+        assertTrue(policyTO.getUsedByResources().contains(RESOURCE_NAME_NOPROPAGATION));
+        assertEquals(Arrays.asList(6L, 7L, 10L, 8L), policyTO.getUsedByRoles());
+    }
+
+    @Test
+    public void getSyncPolicy() {
+        SyncPolicyTO policyTO = policyService.read(1L);
+
+        assertNotNull(policyTO);
+        assertTrue(policyTO.getUsedByRoles().isEmpty());
+    }
+
+    @Test
+    public void getGlobalAccountPolicy() {
+        AccountPolicyTO policyTO = policyService.readGlobal(PolicyType.ACCOUNT);
+
+        assertNotNull(policyTO);
+        assertEquals(PolicyType.GLOBAL_ACCOUNT, policyTO.getType());
+    }
+
+    @Test
+    public void getGlobalPasswordPolicy() {
+        PasswordPolicyTO policyTO = policyService.readGlobal(PolicyType.PASSWORD);
+
+        assertNotNull(policyTO);
+        assertEquals(PolicyType.GLOBAL_PASSWORD, policyTO.getType());
+        assertEquals(8, policyTO.getSpecification().getMinLength());
+        assertFalse(policyTO.getUsedByResources().contains(RESOURCE_NAME_NOPROPAGATION));
+    }
+
+    @Test
+    public void getGlobalSyncPolicy() {
+        SyncPolicyTO policyTO = policyService.readGlobal(PolicyType.SYNC);
+
+        assertNotNull(policyTO);
+        assertEquals(PolicyType.GLOBAL_SYNC, policyTO.getType());
+        assertFalse(policyTO.getUsedByResources().contains(RESOURCE_NAME_CSV));
+        assertFalse(policyTO.getUsedByResources().contains(RESOURCE_NAME_WS2));
+        assertTrue(policyTO.getUsedByRoles().isEmpty());
+    }
+
+    @Test
+    public void createWithException() {
+        PasswordPolicyTO policy = new PasswordPolicyTO(true);
+        policy.setSpecification(new PasswordPolicySpec());
+        policy.setDescription("global password policy");
+
+        try {
+            createPolicy(policy);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidPolicy, e.getType());
+        }
+    }
+
+    @Test
+    public void createMissingDescription() {
+        SyncPolicyTO policy = new SyncPolicyTO();
+        policy.setSpecification(new SyncPolicySpec());
+
+        try {
+            createPolicy(policy);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidPolicy, e.getType());
+        }
+    }
+
+    @Test
+    public void create() {
+        SyncPolicyTO policy = buildSyncPolicyTO();
+
+        SyncPolicyTO policyTO = createPolicy(policy);
+
+        assertNotNull(policyTO);
+        assertEquals(PolicyType.SYNC, policyTO.getType());
+        assertEquals(TestSyncRule.class.getName(), policyTO.getSpecification().getUserJavaRule());
+    }
+
+    @Test
+    public void update() {
+        // get global password
+        PasswordPolicyTO globalPolicy = policyService.read(2L);
+
+        PasswordPolicyTO policy = new PasswordPolicyTO();
+        policy.setDescription("A simple password policy");
+        policy.setSpecification(globalPolicy.getSpecification());
+
+        // create a new password policy using global password as a template
+        policy = createPolicy(policy);
+
+        // read new password policy
+        policy = policyService.read(policy.getKey());
+
+        assertNotNull("find to update did not work", policy);
+
+        PasswordPolicySpec policySpec = policy.getSpecification();
+        policySpec.setMaxLength(22);
+        policy.setSpecification(policySpec);
+
+        // update new password policy
+        policyService.update(policy.getKey(), policy);
+        policy = policyService.read(policy.getKey());
+
+        assertNotNull(policy);
+        assertEquals(PolicyType.PASSWORD, policy.getType());
+        assertEquals(22, policy.getSpecification().getMaxLength());
+        assertEquals(8, policy.getSpecification().getMinLength());
+    }
+
+    @Test
+    public void delete() {
+        SyncPolicyTO policy = buildSyncPolicyTO();
+
+        SyncPolicyTO policyTO = createPolicy(policy);
+        assertNotNull(policyTO);
+
+        policyService.delete(policyTO.getKey());
+
+        try {
+            policyService.read(policyTO.getKey());
+            fail();
+        } catch (SyncopeClientException e) {
+            assertNotNull(e);
+        }
+    }
+
+    @Test
+    public void getCorrelationRules() {
+        assertEquals(1, policyService.getSyncCorrelationRuleClasses().size());
+    }
+
+    @Test
+    public void issueSYNCOPE466() {
+        PasswordPolicyTO policy = policyService.read(4L);
+        assertEquals(PolicyType.PASSWORD, policy.getType());
+
+        policy.setType(PolicyType.GLOBAL_PASSWORD);
+        try {
+            policyService.update(policy.getKey(), policy);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidPolicy, e.getType());
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE553() {
+        AccountPolicyTO policy = new AccountPolicyTO(false);
+        policy.setDescription("SYNCOPE553");
+
+        final AccountPolicySpec accountPolicySpec = new AccountPolicySpec();
+        accountPolicySpec.setMinLength(3);
+        accountPolicySpec.setMaxLength(8);
+        policy.setSpecification(accountPolicySpec);
+
+        policy = createPolicy(policy);
+        assertNotNull(policy);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ReportITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ReportITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ReportITCase.java
new file mode 100644
index 0000000..8102fe0
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ReportITCase.java
@@ -0,0 +1,252 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import org.apache.commons.io.IOUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.report.UserReportletConf;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.ReportExecTO;
+import org.apache.syncope.common.lib.to.ReportTO;
+import org.apache.syncope.common.lib.types.ReportExecExportFormat;
+import org.apache.syncope.common.lib.types.ReportExecStatus;
+import org.apache.syncope.common.lib.wrap.ReportletConfClass;
+import org.apache.syncope.common.rest.api.service.ReportService;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class ReportITCase extends AbstractITCase {
+
+    private ReportTO createReport(final ReportTO report) {
+        Response response = reportService.create(report);
+        assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatusInfo().getStatusCode());
+        return getObject(response.getLocation(), ReportService.class, ReportTO.class);
+    }
+
+    @Test
+    public void getReportletClasses() {
+        List<ReportletConfClass> reportletClasses = reportService.getReportletConfClasses();
+        assertNotNull(reportletClasses);
+        assertFalse(reportletClasses.isEmpty());
+    }
+
+    @Test
+    public void list() {
+        PagedResult<ReportTO> reports = reportService.list();
+        assertNotNull(reports);
+        assertFalse(reports.getResult().isEmpty());
+        for (ReportTO report : reports.getResult()) {
+            assertNotNull(report);
+        }
+    }
+
+    @Test
+    public void read() {
+        ReportTO reportTO = reportService.read(1L);
+
+        assertNotNull(reportTO);
+        assertNotNull(reportTO.getExecutions());
+        assertFalse(reportTO.getExecutions().isEmpty());
+    }
+
+    @Test
+    public void readExecution() {
+        ReportExecTO reportExecTO = reportService.readExecution(1L);
+        assertNotNull(reportExecTO);
+    }
+
+    @Test
+    public void create() {
+        ReportTO report = new ReportTO();
+        report.setName("testReportForCreate" + getUUIDString());
+        report.getReportletConfs().add(new UserReportletConf("first"));
+        report.getReportletConfs().add(new UserReportletConf("second"));
+
+        report = createReport(report);
+        assertNotNull(report);
+
+        ReportTO actual = reportService.read(report.getKey());
+        assertNotNull(actual);
+
+        assertEquals(actual, report);
+    }
+
+    @Test
+    public void update() {
+        ReportTO report = new ReportTO();
+        report.setName("testReportForUpdate" + getUUIDString());
+
+        report.getReportletConfs().add(new UserReportletConf("first"));
+        report.getReportletConfs().add(new UserReportletConf("second"));
+
+        report = createReport(report);
+        assertNotNull(report);
+        assertEquals(2, report.getReportletConfs().size());
+
+        report.getReportletConfs().add(new UserReportletConf("last"));
+
+        reportService.update(report.getKey(), report);
+        ReportTO updated = reportService.read(report.getKey());
+        assertNotNull(updated);
+        assertEquals(3, updated.getReportletConfs().size());
+    }
+
+    @Test
+    public void delete() {
+        ReportTO report = new ReportTO();
+        report.setName("testReportForDelete" + getUUIDString());
+        report.getReportletConfs().add(new UserReportletConf("first"));
+        report.getReportletConfs().add(new UserReportletConf("second"));
+
+        report = createReport(report);
+        assertNotNull(report);
+
+        reportService.delete(report.getKey());
+
+        try {
+            reportService.read(report.getKey());
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
+        }
+    }
+
+    private void checkExport(final Long execId, final ReportExecExportFormat fmt) throws IOException {
+        final Response response = reportService.exportExecutionResult(execId, fmt);
+        assertNotNull(response);
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatusInfo().getStatusCode());
+        assertNotNull(response.getHeaderString(HttpHeaders.CONTENT_DISPOSITION));
+        assertTrue(response.getHeaderString(HttpHeaders.CONTENT_DISPOSITION).
+                endsWith("." + fmt.name().toLowerCase()));
+
+        Object entity = response.getEntity();
+        assertTrue(entity instanceof InputStream);
+        assertFalse(IOUtils.toString((InputStream) entity, SyncopeConstants.DEFAULT_ENCODING).isEmpty());
+    }
+
+    @Test
+    public void executeAndExport() throws IOException {
+        ReportTO reportTO = reportService.read(1L);
+        reportTO.setKey(0);
+        reportTO.setName("executeAndExport" + getUUIDString());
+        reportTO.getExecutions().clear();
+        reportTO = createReport(reportTO);
+        assertNotNull(reportTO);
+
+        ReportExecTO execution = reportService.execute(reportTO.getKey());
+        assertNotNull(execution);
+
+        int i = 0;
+        int maxit = 50;
+
+        // wait for report execution completion (executions incremented)
+        do {
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+            }
+
+            reportTO = reportService.read(reportTO.getKey());
+
+            assertNotNull(reportTO);
+            assertNotNull(reportTO.getExecutions());
+
+            i++;
+        } while (reportTO.getExecutions().isEmpty()
+                || (!ReportExecStatus.SUCCESS.name().equals(reportTO.getExecutions().get(0).getStatus()) && i < maxit));
+        assertEquals(ReportExecStatus.SUCCESS.name(), reportTO.getExecutions().get(0).getStatus());
+
+        long execId = reportTO.getExecutions().get(0).getKey();
+
+        checkExport(execId, ReportExecExportFormat.XML);
+        checkExport(execId, ReportExecExportFormat.HTML);
+        checkExport(execId, ReportExecExportFormat.PDF);
+        checkExport(execId, ReportExecExportFormat.RTF);
+        checkExport(execId, ReportExecExportFormat.CSV);
+    }
+
+    @Test
+    public void issueSYNCOPE43() {
+        ReportTO reportTO = new ReportTO();
+        reportTO.setName("issueSYNCOPE43" + getUUIDString());
+        reportTO = createReport(reportTO);
+        assertNotNull(reportTO);
+
+        ReportExecTO execution = reportService.execute(reportTO.getKey());
+        assertNotNull(execution);
+
+        int maxit = 50;
+        do {
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+            }
+
+            reportTO = reportService.read(reportTO.getKey());
+
+            maxit--;
+        } while (reportTO.getExecutions().isEmpty() && maxit > 0);
+
+        assertEquals(1, reportTO.getExecutions().size());
+    }
+
+    @Test
+    public void issueSYNCOPE102() throws IOException {
+        // Create
+        ReportTO reportTO = reportService.read(1L);
+        reportTO.setKey(0);
+        reportTO.setName("issueSYNCOPE102" + getUUIDString());
+        reportTO = createReport(reportTO);
+        assertNotNull(reportTO);
+
+        // Execute (multiple requests)
+        for (int i = 0; i < 10; i++) {
+            ReportExecTO execution = reportService.execute(reportTO.getKey());
+            assertNotNull(execution);
+        }
+
+        // Wait for one execution
+        int maxit = 50;
+        do {
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+            }
+
+            reportTO = reportService.read(reportTO.getKey());
+
+            maxit--;
+        } while (reportTO.getExecutions().isEmpty() && maxit > 0);
+        assertFalse(reportTO.getExecutions().isEmpty());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ResourceITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ResourceITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ResourceITCase.java
new file mode 100644
index 0000000..6cb624a
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/ResourceITCase.java
@@ -0,0 +1,590 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+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.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.BulkAction;
+import org.apache.syncope.common.lib.to.MappingItemTO;
+import org.apache.syncope.common.lib.to.MappingTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.ConnConfPropSchema;
+import org.apache.syncope.common.lib.types.ConnConfProperty;
+import org.apache.syncope.common.lib.types.EntityViolationType;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.wrap.PropagationActionClass;
+import org.apache.syncope.common.rest.api.service.ResourceService;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class ResourceITCase extends AbstractITCase {
+
+    private ResourceTO buildResourceTO(final String resourceName) {
+        ResourceTO resourceTO = new ResourceTO();
+
+        resourceTO.setKey(resourceName);
+        resourceTO.setConnectorId(102L);
+
+        MappingTO mapping = new MappingTO();
+
+        MappingItemTO item = new MappingItemTO();
+        item.setExtAttrName("userId");
+        item.setIntAttrName("userId");
+        item.setIntMappingType(IntMappingType.UserPlainSchema);
+        item.setPurpose(MappingPurpose.BOTH);
+        mapping.addItem(item);
+
+        item = new MappingItemTO();
+        item.setExtAttrName("username");
+        item.setIntAttrName("fullname");
+        item.setIntMappingType(IntMappingType.UserId);
+        item.setPurpose(MappingPurpose.BOTH);
+        mapping.setAccountIdItem(item);
+
+        item = new MappingItemTO();
+        item.setExtAttrName("fullname");
+        item.setIntAttrName("cn");
+        item.setIntMappingType(IntMappingType.UserPlainSchema);
+        item.setAccountid(false);
+        item.setPurpose(MappingPurpose.BOTH);
+        mapping.addItem(item);
+
+        resourceTO.setUmapping(mapping);
+        return resourceTO;
+    }
+
+    @Test
+    public void getPropagationActionsClasses() {
+        List<PropagationActionClass> actions = resourceService.getPropagationActionsClasses();
+        assertNotNull(actions);
+        assertFalse(actions.isEmpty());
+    }
+
+    @Test
+    public void create() {
+        String resourceName = RESOURCE_NAME_CREATE;
+        ResourceTO resourceTO = buildResourceTO(resourceName);
+
+        Response response = resourceService.create(resourceTO);
+        ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+        assertNotNull(actual);
+
+        // check for existence
+        actual = resourceService.read(resourceName);
+        assertNotNull(actual);
+    }
+
+    @Test
+    public void createOverridingProps() {
+        String resourceName = "overriding-conn-conf-target-resource-create";
+        ResourceTO resourceTO = new ResourceTO();
+
+        MappingTO mapping = new MappingTO();
+
+        MappingItemTO item = new MappingItemTO();
+        item.setExtAttrName("uid");
+        item.setIntAttrName("userId");
+        item.setIntMappingType(IntMappingType.UserPlainSchema);
+        item.setPurpose(MappingPurpose.BOTH);
+        mapping.addItem(item);
+
+        item = new MappingItemTO();
+        item.setExtAttrName("username");
+        item.setIntAttrName("fullname");
+        item.setIntMappingType(IntMappingType.UserId);
+        item.setAccountid(true);
+        item.setPurpose(MappingPurpose.BOTH);
+        mapping.setAccountIdItem(item);
+
+        item = new MappingItemTO();
+        item.setExtAttrName("fullname");
+        item.setIntAttrName("cn");
+        item.setIntMappingType(IntMappingType.UserPlainSchema);
+        item.setAccountid(false);
+        item.setPurpose(MappingPurpose.BOTH);
+        mapping.addItem(item);
+
+        resourceTO.setKey(resourceName);
+        resourceTO.setConnectorId(102L);
+
+        resourceTO.setUmapping(mapping);
+
+        ConnConfProperty p = new ConnConfProperty();
+        ConnConfPropSchema schema = new ConnConfPropSchema();
+        schema.setType("java.lang.String");
+        schema.setName("endpoint");
+        schema.setRequired(true);
+        p.setSchema(schema);
+        p.getValues().add("http://invalidurl/");
+
+        Set<ConnConfProperty> connectorConfigurationProperties = new HashSet<ConnConfProperty>(Arrays.asList(p));
+        resourceTO.getConnConfProperties().addAll(connectorConfigurationProperties);
+
+        Response response = resourceService.create(resourceTO);
+        ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+        assertNotNull(actual);
+
+        // check the existence
+        actual = resourceService.read(resourceName);
+        assertNotNull(actual);
+    }
+
+    @Test
+    public void createWithSingleMappingItem() {
+        String resourceName = RESOURCE_NAME_CREATE_SINGLE;
+        ResourceTO resourceTO = new ResourceTO();
+        resourceTO.setKey(resourceName);
+        resourceTO.setConnectorId(102L);
+
+        MappingTO umapping = new MappingTO();
+
+        MappingItemTO item = new MappingItemTO();
+        item.setIntMappingType(IntMappingType.UserId);
+        item.setExtAttrName("userId");
+        item.setAccountid(true);
+        item.setPurpose(MappingPurpose.PROPAGATION);
+        umapping.setAccountIdItem(item);
+
+        resourceTO.setUmapping(umapping);
+
+        MappingTO rmapping = new MappingTO();
+
+        item = new MappingItemTO();
+        item.setIntMappingType(IntMappingType.RoleId);
+        item.setExtAttrName("roleId");
+        item.setAccountid(true);
+        item.setPurpose(MappingPurpose.SYNCHRONIZATION);
+        rmapping.setAccountIdItem(item);
+
+        resourceTO.setRmapping(rmapping);
+
+        Response response = resourceService.create(resourceTO);
+        ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+
+        assertNotNull(actual);
+        assertNotNull(actual.getUmapping());
+        assertNotNull(actual.getUmapping().getItems());
+        assertNotNull(actual.getRmapping());
+        assertNotNull(actual.getRmapping().getItems());
+        assertEquals(MappingPurpose.SYNCHRONIZATION, actual.getRmapping().getAccountIdItem().getPurpose());
+        assertEquals(MappingPurpose.PROPAGATION, actual.getUmapping().getAccountIdItem().getPurpose());
+    }
+
+    @Test
+    public void createWithInvalidMapping() {
+        String resourceName = RESOURCE_NAME_CREATE_WRONG;
+        ResourceTO resourceTO = new ResourceTO();
+        resourceTO.setKey(resourceName);
+        resourceTO.setConnectorId(102L);
+
+        MappingTO mapping = new MappingTO();
+
+        MappingItemTO item = new MappingItemTO();
+        item.setIntMappingType(IntMappingType.UserId);
+        item.setExtAttrName("userId");
+        item.setAccountid(true);
+        mapping.setAccountIdItem(item);
+
+        item = new MappingItemTO();
+        item.setIntMappingType(IntMappingType.UserPlainSchema);
+        item.setExtAttrName("email");
+        // missing intAttrName ...
+        mapping.addItem(item);
+
+        resourceTO.setUmapping(mapping);
+
+        try {
+            createResource(resourceTO);
+            fail("Create should not have worked");
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
+            assertEquals("intAttrName", e.getElements().iterator().next());
+        }
+    }
+
+    @Test(expected = SyncopeClientException.class)
+    public void createWithoutExtAttr() {
+        String resourceName = RESOURCE_NAME_CREATE_WRONG;
+        ResourceTO resourceTO = new ResourceTO();
+        resourceTO.setKey(resourceName);
+        resourceTO.setConnectorId(102L);
+
+        MappingTO mapping = new MappingTO();
+
+        MappingItemTO item = new MappingItemTO();
+        item.setIntMappingType(IntMappingType.UserId);
+        item.setExtAttrName("userId");
+        item.setAccountid(true);
+        mapping.setAccountIdItem(item);
+
+        item = new MappingItemTO();
+        item.setIntMappingType(IntMappingType.UserPlainSchema);
+        item.setIntAttrName("usernane");
+        // missing extAttrName ...
+        mapping.addItem(item);
+
+        resourceTO.setUmapping(mapping);
+
+        createResource(resourceTO);
+    }
+
+    @Test
+    public void createWithPasswordPolicy() {
+        String resourceName = "res-with-password-policy";
+        ResourceTO resourceTO = new ResourceTO();
+        resourceTO.setKey(resourceName);
+        resourceTO.setConnectorId(102L);
+        resourceTO.setPasswordPolicy(4L);
+
+        MappingTO mapping = new MappingTO();
+
+        MappingItemTO item = new MappingItemTO();
+        item.setExtAttrName("userId");
+        item.setIntAttrName("userId");
+        item.setIntMappingType(IntMappingType.UserPlainSchema);
+        item.setAccountid(true);
+        item.setPurpose(MappingPurpose.BOTH);
+        mapping.setAccountIdItem(item);
+
+        resourceTO.setUmapping(mapping);
+
+        Response response = resourceService.create(resourceTO);
+        ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+        assertNotNull(actual);
+
+        // check the existence
+        actual = resourceService.read(resourceName);
+        assertNotNull(actual);
+        assertNotNull(actual.getPasswordPolicy());
+        assertEquals(4L, (long) actual.getPasswordPolicy());
+    }
+
+    @Test
+    public void updateWithException() {
+        try {
+            ResourceTO resourceTO = new ResourceTO();
+            resourceTO.setKey("resourcenotfound");
+
+            resourceService.update(resourceTO.getKey(), resourceTO);
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
+        }
+    }
+
+    @Test
+    public void update() {
+        String resourceName = RESOURCE_NAME_UPDATE;
+        ResourceTO resourceTO = new ResourceTO();
+        resourceTO.setKey(resourceName);
+        resourceTO.setConnectorId(101L);
+
+        MappingTO mapping = new MappingTO();
+
+        // Update with an existing and already assigned mapping
+        MappingItemTO item = new MappingItemTO();
+        item.setKey(112L);
+        item.setExtAttrName("test3");
+        item.setIntAttrName("fullname");
+        item.setIntMappingType(IntMappingType.UserPlainSchema);
+        item.setPurpose(MappingPurpose.BOTH);
+        mapping.addItem(item);
+
+        // Update defining new mappings
+        for (int i = 4; i < 6; i++) {
+            item = new MappingItemTO();
+            item.setExtAttrName("test" + i);
+            item.setIntAttrName("fullname");
+            item.setIntMappingType(IntMappingType.UserPlainSchema);
+            item.setPurpose(MappingPurpose.BOTH);
+            mapping.addItem(item);
+        }
+        item = new MappingItemTO();
+        item.setExtAttrName("username");
+        item.setIntAttrName("fullname");
+        item.setIntMappingType(IntMappingType.UserId);
+        item.setAccountid(true);
+        item.setPurpose(MappingPurpose.BOTH);
+        mapping.setAccountIdItem(item);
+
+        resourceTO.setUmapping(mapping);
+
+        resourceService.update(resourceTO.getKey(), resourceTO);
+        ResourceTO actual = resourceService.read(resourceTO.getKey());
+        assertNotNull(actual);
+
+        // check for existence
+        Collection<MappingItemTO> mapItems = actual.getUmapping().getItems();
+        assertNotNull(mapItems);
+        assertEquals(4, mapItems.size());
+    }
+
+    @Test
+    public void deleteWithException() {
+        try {
+            resourceService.delete("resourcenotfound");
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
+        }
+    }
+
+    @Test
+    public void updateResetSyncToken() {
+        // create resource with sync token
+        String resourceName = RESOURCE_NAME_RESETSYNCTOKEN + getUUIDString();
+        ResourceTO pre = buildResourceTO(resourceName);
+        pre.setUsyncToken("test");
+        resourceService.create(pre);
+
+        pre.setUsyncToken(null);
+        resourceService.update(pre.getKey(), pre);
+        ResourceTO actual = resourceService.read(pre.getKey());
+        // check that the synctoken has been reset
+        assertNull(actual.getUsyncToken());
+    }
+
+    @Test
+    public void delete() {
+        String resourceName = "tobedeleted";
+
+        ResourceTO resource = buildResourceTO(resourceName);
+        Response response = resourceService.create(resource);
+        ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+        assertNotNull(actual);
+
+        resourceService.delete(resourceName);
+
+        try {
+            resourceService.read(resourceName);
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
+        }
+    }
+
+    @Test
+    public void list() {
+        List<ResourceTO> actuals = resourceService.list();
+        assertNotNull(actuals);
+        assertFalse(actuals.isEmpty());
+        for (ResourceTO resourceTO : actuals) {
+            assertNotNull(resourceTO);
+        }
+    }
+
+    @Test
+    public void listByType() {
+        List<ResourceTO> actuals = resourceService.list(105L);
+        assertNotNull(actuals);
+
+        for (ResourceTO resourceTO : actuals) {
+            assertNotNull(resourceTO);
+            assertEquals(105L, resourceTO.getConnectorId().longValue());
+        }
+    }
+
+    @Test
+    public void read() {
+        ResourceTO actual = resourceService.read(RESOURCE_NAME_TESTDB);
+        assertNotNull(actual);
+    }
+
+    @Test
+    public void issueSYNCOPE323() {
+        ResourceTO actual = resourceService.read(RESOURCE_NAME_TESTDB);
+        assertNotNull(actual);
+
+        try {
+            createResource(actual);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.CONFLICT, e.getType().getResponseStatus());
+            assertEquals(ClientExceptionType.EntityExists, e.getType());
+        }
+
+        actual.setKey(null);
+        try {
+            createResource(actual);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.BAD_REQUEST, e.getType().getResponseStatus());
+            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
+        }
+    }
+
+    @Test
+    public void bulkAction() {
+        resourceService.create(buildResourceTO("forBulk1"));
+        resourceService.create(buildResourceTO("forBulk2"));
+
+        assertNotNull(resourceService.read("forBulk1"));
+        assertNotNull(resourceService.read("forBulk2"));
+
+        final BulkAction bulkAction = new BulkAction();
+        bulkAction.setOperation(BulkAction.Type.DELETE);
+
+        bulkAction.getTargets().add(String.valueOf("forBulk1"));
+        bulkAction.getTargets().add(String.valueOf("forBulk2"));
+
+        resourceService.bulk(bulkAction);
+
+        try {
+            resourceService.read("forBulk1");
+            fail();
+        } catch (SyncopeClientException e) {
+        }
+
+        try {
+            resourceService.read("forBulk2");
+            fail();
+        } catch (SyncopeClientException e) {
+        }
+    }
+
+    @Test
+    public void issueSYNCOPE360() {
+        final String name = "SYNCOPE360-" + getUUIDString();
+        resourceService.create(buildResourceTO(name));
+
+        ResourceTO resource = resourceService.read(name);
+        assertNotNull(resource);
+        assertNotNull(resource.getUmapping());
+
+        resource.setUmapping(new MappingTO());
+        resourceService.update(name, resource);
+
+        resource = resourceService.read(name);
+        assertNotNull(resource);
+        assertNull(resource.getUmapping());
+    }
+
+    @Test
+    public void issueSYNCOPE368() {
+        final String name = "SYNCOPE368-" + getUUIDString();
+
+        ResourceTO resourceTO = new ResourceTO();
+
+        resourceTO.setKey(name);
+        resourceTO.setConnectorId(105L);
+
+        MappingTO mapping = new MappingTO();
+
+        MappingItemTO item = new MappingItemTO();
+        item.setIntMappingType(IntMappingType.RoleName);
+        item.setExtAttrName("cn");
+        item.setPurpose(MappingPurpose.BOTH);
+        mapping.setAccountIdItem(item);
+
+        item = new MappingItemTO();
+        item.setIntMappingType(IntMappingType.RoleOwnerSchema);
+        item.setExtAttrName("owner");
+        item.setPurpose(MappingPurpose.BOTH);
+        mapping.addItem(item);
+
+        resourceTO.setRmapping(mapping);
+
+        resourceTO = createResource(resourceTO);
+        assertNotNull(resourceTO);
+        assertEquals(2, resourceTO.getRmapping().getItems().size());
+    }
+
+    @Test
+    public void issueSYNCOPE418() {
+        try {
+            resourceService.create(
+                    buildResourceTO("http://schemas.examples.org/security/authorization/organizationUnit"));
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidExternalResource, e.getType());
+
+            assertTrue(e.getElements().iterator().next().toString().contains(EntityViolationType.InvalidName.name()));
+        }
+    }
+
+    @Test
+    public void anonymous() {
+        ResourceService unauthenticated = clientFactory.createAnonymous().getService(ResourceService.class);
+        try {
+            unauthenticated.list();
+            fail();
+        } catch (AccessControlException e) {
+            assertNotNull(e);
+        }
+
+        ResourceService anonymous = clientFactory.create(ANONYMOUS_UNAME, ANONYMOUS_KEY).
+                getService(ResourceService.class);
+        assertFalse(anonymous.list().isEmpty());
+    }
+
+    @Test
+    public void issueSYNCOPE493() {
+        // create resource with attribute mapping set to NONE and check its propagation
+        String resourceName = RESOURCE_NAME_CREATE_NONE;
+        ResourceTO resourceTO = new ResourceTO();
+        resourceTO.setKey(resourceName);
+        resourceTO.setConnectorId(102L);
+
+        MappingTO umapping = new MappingTO();
+
+        MappingItemTO item = new MappingItemTO();
+        item.setIntMappingType(IntMappingType.UserId);
+        item.setExtAttrName("userId");
+        item.setAccountid(true);
+        item.setPurpose(MappingPurpose.PROPAGATION);
+        umapping.setAccountIdItem(item);
+
+        MappingItemTO item2 = new MappingItemTO();
+        item2.setIntMappingType(IntMappingType.UserPlainSchema);
+        item2.setAccountid(false);
+        item2.setIntAttrName("gender");
+        item2.setExtAttrName("gender");
+        item2.setPurpose(MappingPurpose.NONE);
+        umapping.addItem(item2);
+
+        resourceTO.setUmapping(umapping);
+
+        Response response = resourceService.create(resourceTO);
+        ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+
+        assertNotNull(actual);
+        assertNotNull(actual.getUmapping());
+        assertNotNull(actual.getUmapping().getItems());
+        assertEquals(MappingPurpose.PROPAGATION, actual.getUmapping().getAccountIdItem().getPurpose());
+        for (MappingItemTO itemTO : actual.getUmapping().getItems()) {
+            if ("gender".equals(itemTO.getIntAttrName())) {
+                assertEquals(MappingPurpose.NONE, itemTO.getPurpose());
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/80589a1b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/RoleITCase.java
----------------------------------------------------------------------
diff --git a/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/RoleITCase.java b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/RoleITCase.java
new file mode 100644
index 0000000..dbd9532
--- /dev/null
+++ b/syncope620/fit/reference/src/test/java/org/apache/syncope/fit/server/reference/RoleITCase.java
@@ -0,0 +1,797 @@
+/*
+ * 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.server.reference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+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.io.IOException;
+import java.io.InputStream;
+import java.security.AccessControlException;
+import java.util.List;
+import javax.ws.rs.core.Response;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.mod.ReferenceMod;
+import org.apache.syncope.common.lib.mod.RoleMod;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.ConnObjectTO;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.PlainSchemaTO;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.ResourceAssociationActionType;
+import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.syncope.common.lib.types.SubjectType;
+import org.apache.syncope.common.lib.wrap.ResourceName;
+import org.apache.syncope.common.rest.api.CollectionWrapper;
+import org.apache.syncope.common.rest.api.Preference;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.RoleService;
+import org.identityconnectors.framework.common.objects.Name;
+import org.junit.FixMethodOrder;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class RoleITCase extends AbstractITCase {
+
+    private RoleTO buildBasicRoleTO(final String name) {
+        RoleTO roleTO = new RoleTO();
+        roleTO.setName(name + getUUIDString());
+        roleTO.setParent(8L);
+        return roleTO;
+    }
+
+    private RoleTO buildRoleTO(final String name) {
+        RoleTO roleTO = buildBasicRoleTO(name);
+
+        // verify inheritance password and account policies
+        roleTO.setInheritAccountPolicy(false);
+        // not inherited so setter execution shouldn't be ignored
+        roleTO.setAccountPolicy(6L);
+
+        roleTO.setInheritPasswordPolicy(true);
+        // inherited so setter execution should be ignored
+        roleTO.setPasswordPolicy(2L);
+
+        roleTO.getRAttrTemplates().add("icon");
+        roleTO.getPlainAttrs().add(attrTO("icon", "anIcon"));
+
+        roleTO.getResources().add(RESOURCE_NAME_LDAP);
+        return roleTO;
+    }
+
+    @Test
+    public void createWithException() {
+        RoleTO newRoleTO = new RoleTO();
+        newRoleTO.getPlainAttrs().add(attrTO("attr1", "value1"));
+
+        try {
+            createRole(newRoleTO);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidRole, e.getType());
+        }
+    }
+
+    @Test
+    @Ignore
+    public void create() {
+        RoleTO roleTO = buildRoleTO("lastRole");
+        roleTO.getRVirAttrTemplates().add("rvirtualdata");
+        roleTO.getVirAttrs().add(attrTO("rvirtualdata", "rvirtualvalue"));
+        roleTO.setRoleOwner(8L);
+
+        roleTO = createRole(roleTO);
+        assertNotNull(roleTO);
+
+        assertNotNull(roleTO.getVirAttrMap());
+        assertNotNull(roleTO.getVirAttrMap().get("rvirtualdata").getValues());
+        assertFalse(roleTO.getVirAttrMap().get("rvirtualdata").getValues().isEmpty());
+        assertEquals("rvirtualvalue", roleTO.getVirAttrMap().get("rvirtualdata").getValues().get(0));
+
+        assertNotNull(roleTO.getAccountPolicy());
+        assertEquals(6L, (long) roleTO.getAccountPolicy());
+
+        assertNotNull(roleTO.getPasswordPolicy());
+        assertEquals(4L, (long) roleTO.getPasswordPolicy());
+
+        assertTrue(roleTO.getResources().contains(RESOURCE_NAME_LDAP));
+
+        ConnObjectTO connObjectTO =
+                resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, roleTO.getKey());
+        assertNotNull(connObjectTO);
+        assertNotNull(connObjectTO.getPlainAttrMap().get("owner"));
+
+        // SYNCOPE-515: remove ownership
+        final RoleMod roleMod = new RoleMod();
+        roleMod.setKey(roleTO.getKey());
+        roleMod.setRoleOwner(new ReferenceMod());
+
+        assertNull(updateRole(roleMod).getRoleOwner());
+    }
+
+    @Test
+    public void createWithPasswordPolicy() {
+        RoleTO roleTO = new RoleTO();
+        roleTO.setName("roleWithPassword" + getUUIDString());
+        roleTO.setParent(8L);
+        roleTO.setPasswordPolicy(4L);
+
+        RoleTO actual = createRole(roleTO);
+        assertNotNull(actual);
+
+        actual = roleService.read(actual.getKey());
+        assertNotNull(actual);
+        assertNotNull(actual.getPasswordPolicy());
+        assertEquals(4L, (long) actual.getPasswordPolicy());
+    }
+
+    @Test
+    public void delete() {
+        try {
+            roleService.delete(0L);
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
+        }
+
+        RoleTO roleTO = new RoleTO();
+        roleTO.setName("toBeDeleted" + getUUIDString());
+        roleTO.setParent(8L);
+
+        roleTO.getResources().add(RESOURCE_NAME_LDAP);
+
+        roleTO = createRole(roleTO);
+        assertNotNull(roleTO);
+
+        RoleTO deletedRole = deleteRole(roleTO.getKey());
+        assertNotNull(deletedRole);
+
+        try {
+            roleService.read(deletedRole.getKey());
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
+        }
+    }
+
+    @Test
+    public void list() {
+        PagedResult<RoleTO> roleTOs = roleService.list();
+        assertNotNull(roleTOs);
+        assertTrue(roleTOs.getResult().size() >= 8);
+        for (RoleTO roleTO : roleTOs.getResult()) {
+            assertNotNull(roleTO);
+        }
+    }
+
+    @Test
+    public void parent() {
+        RoleTO roleTO = roleService.parent(7L);
+
+        assertNotNull(roleTO);
+        assertEquals(roleTO.getKey(), 6L);
+    }
+
+    @Test
+    public void read() {
+        RoleTO roleTO = roleService.read(1L);
+
+        assertNotNull(roleTO);
+        assertNotNull(roleTO.getPlainAttrs());
+        assertFalse(roleTO.getPlainAttrs().isEmpty());
+    }
+
+    @Test
+    public void selfRead() {
+        UserTO userTO = userService.read(1L);
+        assertNotNull(userTO);
+
+        assertTrue(userTO.getMembershipMap().containsKey(1L));
+        assertFalse(userTO.getMembershipMap().containsKey(3L));
+
+        RoleService roleService2 = clientFactory.create("rossini", ADMIN_PWD).getService(RoleService.class);
+
+        try {
+            roleService2.readSelf(3L);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.UnauthorizedRole, e.getType());
+        }
+
+        RoleTO roleTO = roleService2.readSelf(1L);
+        assertNotNull(roleTO);
+        assertNotNull(roleTO.getPlainAttrs());
+        assertFalse(roleTO.getPlainAttrs().isEmpty());
+    }
+
+    @Test
+    public void update() {
+        RoleTO roleTO = buildRoleTO("latestRole" + getUUIDString());
+        roleTO.getRAttrTemplates().add("show");
+        roleTO = createRole(roleTO);
+
+        assertEquals(1, roleTO.getPlainAttrs().size());
+
+        assertNotNull(roleTO.getAccountPolicy());
+        assertEquals(6L, (long) roleTO.getAccountPolicy());
+
+        assertNotNull(roleTO.getPasswordPolicy());
+        assertEquals(4L, (long) roleTO.getPasswordPolicy());
+
+        RoleMod roleMod = new RoleMod();
+        roleMod.setKey(roleTO.getKey());
+        String modName = "finalRole" + getUUIDString();
+        roleMod.setName(modName);
+        roleMod.getPlainAttrsToUpdate().add(attrMod("show", "FALSE"));
+
+        // change password policy inheritance
+        roleMod.setInheritPasswordPolicy(Boolean.FALSE);
+
+        roleTO = updateRole(roleMod);
+
+        assertEquals(modName, roleTO.getName());
+        assertEquals(2, roleTO.getPlainAttrs().size());
+
+        // changes ignored because not requested (null ReferenceMod)
+        assertNotNull(roleTO.getAccountPolicy());
+        assertEquals(6L, (long) roleTO.getAccountPolicy());
+
+        // password policy null because not inherited
+        assertNull(roleTO.getPasswordPolicy());
+    }
+
+    @Test
+    public void updateRemovingVirAttribute() {
+        RoleTO roleTO = buildBasicRoleTO("withvirtual" + getUUIDString());
+        roleTO.getRVirAttrTemplates().add("rvirtualdata");
+        roleTO.getVirAttrs().add(attrTO("rvirtualdata", null));
+
+        roleTO = createRole(roleTO);
+
+        assertNotNull(roleTO);
+        assertEquals(1, roleTO.getVirAttrs().size());
+
+        final RoleMod roleMod = new RoleMod();
+        roleMod.setKey(roleTO.getKey());
+        roleMod.getVirAttrsToRemove().add("rvirtualdata");
+
+        roleTO = updateRole(roleMod);
+        assertNotNull(roleTO);
+        assertTrue(roleTO.getVirAttrs().isEmpty());
+    }
+
+    @Test
+    public void updateRemovingDerAttribute() {
+        RoleTO roleTO = buildBasicRoleTO("withderived" + getUUIDString());
+        roleTO.getRDerAttrTemplates().add("rderivedschema");
+        roleTO.getDerAttrs().add(attrTO("rderivedschema", null));
+
+        roleTO = createRole(roleTO);
+
+        assertNotNull(roleTO);
+        assertEquals(1, roleTO.getDerAttrs().size());
+
+        final RoleMod roleMod = new RoleMod();
+        roleMod.setKey(roleTO.getKey());
+        roleMod.getDerAttrsToRemove().add("rderivedschema");
+
+        roleTO = updateRole(roleMod);
+        assertNotNull(roleTO);
+        assertTrue(roleTO.getDerAttrs().isEmpty());
+    }
+
+    @Test
+    public void updateAsRoleOwner() {
+        // 1. read role as admin
+        RoleTO roleTO = roleService.read(7L);
+
+        // issue SYNCOPE-15
+        assertNotNull(roleTO.getCreationDate());
+        assertNotNull(roleTO.getLastChangeDate());
+        assertEquals("admin", roleTO.getCreator());
+        assertEquals("admin", roleTO.getLastModifier());
+
+        // 2. prepare update
+        RoleMod roleMod = new RoleMod();
+        roleMod.setKey(roleTO.getKey());
+        roleMod.setName("Managing Director");
+
+        // 3. try to update as verdi, not owner of role 7 - fail
+        RoleService roleService2 = clientFactory.create("verdi", ADMIN_PWD).getService(RoleService.class);
+
+        try {
+            roleService2.update(roleMod.getKey(), roleMod);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.UNAUTHORIZED, e.getType().getResponseStatus());
+        } catch (AccessControlException e) {
+            assertNotNull(e);
+        }
+
+        // 4. update as puccini, owner of role 7 because owner of role 6 with inheritance - success
+        RoleService roleService3 = clientFactory.create("puccini", ADMIN_PWD).getService(RoleService.class);
+
+        roleTO = roleService3.update(roleMod.getKey(), roleMod).readEntity(RoleTO.class);
+        assertEquals("Managing Director", roleTO.getName());
+
+        // issue SYNCOPE-15
+        assertNotNull(roleTO.getCreationDate());
+        assertNotNull(roleTO.getLastChangeDate());
+        assertEquals("admin", roleTO.getCreator());
+        assertEquals("puccini", roleTO.getLastModifier());
+        assertTrue(roleTO.getCreationDate().before(roleTO.getLastChangeDate()));
+    }
+
+    /**
+     * Role rename used to fail in case of parent null.
+     *
+     * http://code.google.com/p/syncope/issues/detail?id=178
+     */
+    @Test
+    public void issue178() {
+        RoleTO roleTO = new RoleTO();
+        String roleName = "torename" + getUUIDString();
+        roleTO.setName(roleName);
+
+        RoleTO actual = createRole(roleTO);
+
+        assertNotNull(actual);
+        assertEquals(roleName, actual.getName());
+        assertEquals(0L, actual.getParent());
+
+        RoleMod roleMod = new RoleMod();
+        roleMod.setKey(actual.getKey());
+        String renamedRole = "renamed" + getUUIDString();
+        roleMod.setName(renamedRole);
+
+        actual = updateRole(roleMod);
+        assertNotNull(actual);
+        assertEquals(renamedRole, actual.getName());
+        assertEquals(0L, actual.getParent());
+    }
+
+    @Test
+    public void issueSYNCOPE228() {
+        RoleTO roleTO = buildRoleTO("issueSYNCOPE228");
+        roleTO.getEntitlements().add("USER_READ");
+        roleTO.getEntitlements().add("SCHEMA_READ");
+
+        roleTO = createRole(roleTO);
+        assertNotNull(roleTO);
+        assertNotNull(roleTO.getEntitlements());
+        assertFalse(roleTO.getEntitlements().isEmpty());
+
+        List<String> entitlements = roleTO.getEntitlements();
+
+        RoleMod roleMod = new RoleMod();
+        roleMod.setKey(roleTO.getKey());
+        roleMod.setInheritDerAttrs(Boolean.TRUE);
+
+        roleTO = updateRole(roleMod);
+        assertNotNull(roleTO);
+        assertEquals(entitlements, roleTO.getEntitlements());
+
+        roleMod = new RoleMod();
+        roleMod.setKey(roleTO.getKey());
+        roleMod.setModEntitlements(true);
+        roleMod.getEntitlements().clear();
+
+        roleTO = updateRole(roleMod);
+        assertNotNull(roleTO);
+        assertTrue(roleTO.getEntitlements().isEmpty());
+    }
+
+    @Test
+    public void unlink() {
+        RoleTO actual = createRole(buildRoleTO("unlink"));
+        assertNotNull(actual);
+
+        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, actual.getKey()));
+
+        assertNotNull(roleService.bulkDeassociation(actual.getKey(),
+                ResourceDeassociationActionType.UNLINK,
+                CollectionWrapper.wrap(RESOURCE_NAME_LDAP, ResourceName.class)).
+                readEntity(BulkActionResult.class));
+
+        actual = roleService.read(actual.getKey());
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+
+        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, actual.getKey()));
+    }
+
+    @Test
+    public void link() {
+        RoleTO roleTO = buildRoleTO("link");
+        roleTO.getResources().clear();
+
+        RoleTO actual = createRole(roleTO);
+        assertNotNull(actual);
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, actual.getKey());
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+
+        assertNotNull(roleService.bulkAssociation(actual.getKey(),
+                ResourceAssociationActionType.LINK,
+                CollectionWrapper.wrap(RESOURCE_NAME_LDAP, ResourceName.class)).
+                readEntity(BulkActionResult.class));
+
+        actual = roleService.read(actual.getKey());
+        assertFalse(actual.getResources().isEmpty());
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, actual.getKey());
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+    }
+
+    @Test
+    public void unassign() {
+        RoleTO actual = createRole(buildRoleTO("unassign"));
+        assertNotNull(actual);
+
+        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, actual.getKey()));
+
+        assertNotNull(roleService.bulkDeassociation(actual.getKey(),
+                ResourceDeassociationActionType.UNASSIGN,
+                CollectionWrapper.wrap(RESOURCE_NAME_LDAP, ResourceName.class)).
+                readEntity(BulkActionResult.class));
+
+        actual = roleService.read(actual.getKey());
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, actual.getKey());
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+    }
+
+    @Test
+    public void assign() {
+        RoleTO roleTO = buildRoleTO("assign");
+        roleTO.getResources().clear();
+
+        RoleTO actual = createRole(roleTO);
+        assertNotNull(actual);
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, actual.getKey());
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+
+        assertNotNull(roleService.bulkAssociation(actual.getKey(),
+                ResourceAssociationActionType.ASSIGN,
+                CollectionWrapper.wrap(RESOURCE_NAME_LDAP, ResourceName.class)).
+                readEntity(BulkActionResult.class));
+
+        actual = roleService.read(actual.getKey());
+        assertFalse(actual.getResources().isEmpty());
+        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, actual.getKey()));
+    }
+
+    @Test
+    public void deprovision() {
+        RoleTO actual = createRole(buildRoleTO("deprovision"));
+        assertNotNull(actual);
+
+        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, actual.getKey()));
+
+        assertNotNull(roleService.bulkDeassociation(actual.getKey(),
+                ResourceDeassociationActionType.DEPROVISION,
+                CollectionWrapper.wrap(RESOURCE_NAME_LDAP, ResourceName.class)).
+                readEntity(BulkActionResult.class));
+
+        actual = roleService.read(actual.getKey());
+        assertNotNull(actual);
+        assertFalse(actual.getResources().isEmpty());
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, actual.getKey());
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+    }
+
+    @Test
+    public void provision() {
+        RoleTO roleTO = buildRoleTO("assign");
+        roleTO.getResources().clear();
+
+        RoleTO actual = createRole(roleTO);
+        assertNotNull(actual);
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, actual.getKey());
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+
+        assertNotNull(roleService.bulkAssociation(actual.getKey(),
+                ResourceAssociationActionType.PROVISION,
+                CollectionWrapper.wrap(RESOURCE_NAME_LDAP, ResourceName.class)).
+                readEntity(BulkActionResult.class));
+
+        actual = roleService.read(actual.getKey());
+        assertTrue(actual.getResources().isEmpty());
+
+        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, actual.getKey()));
+    }
+
+    @Test
+    public void deprovisionUnlinked() {
+        RoleTO roleTO = buildRoleTO("assign");
+        roleTO.getResources().clear();
+
+        RoleTO actual = createRole(roleTO);
+        assertNotNull(actual);
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, actual.getKey());
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+
+        assertNotNull(roleService.bulkAssociation(actual.getKey(),
+                ResourceAssociationActionType.PROVISION,
+                CollectionWrapper.wrap("resource-ldap", ResourceName.class)).
+                readEntity(BulkActionResult.class));
+
+        actual = roleService.read(actual.getKey());
+        assertTrue(actual.getResources().isEmpty());
+
+        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, actual.getKey()));
+
+        assertNotNull(roleService.bulkDeassociation(actual.getKey(),
+                ResourceDeassociationActionType.DEPROVISION,
+                CollectionWrapper.wrap(RESOURCE_NAME_LDAP, ResourceName.class)).
+                readEntity(BulkActionResult.class));
+
+        actual = roleService.read(actual.getKey());
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+
+        try {
+            resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, actual.getKey());
+            fail();
+        } catch (Exception e) {
+            assertNotNull(e);
+        }
+    }
+
+    @Test
+    public void createWithMandatorySchemaNotTemplate() {
+        // 1. create a role mandatory schema
+        PlainSchemaTO badge = new PlainSchemaTO();
+        badge.setKey("badge");
+        badge.setMandatoryCondition("true");
+        schemaService.create(AttributableType.ROLE, SchemaType.PLAIN, badge);
+
+        // 2. create a role *without* an attribute for that schema: it works
+        RoleTO roleTO = buildRoleTO("lastRole");
+        assertFalse(roleTO.getPlainAttrMap().containsKey(badge.getKey()));
+        roleTO = createRole(roleTO);
+        assertNotNull(roleTO);
+        assertFalse(roleTO.getPlainAttrMap().containsKey(badge.getKey()));
+
+        // 3. add a template for badge to the role just created - 
+        // failure since no values are provided and it is mandatory
+        RoleMod roleMod = new RoleMod();
+        roleMod.setKey(roleTO.getKey());
+        roleMod.setModRAttrTemplates(true);
+        roleMod.getRPlainAttrTemplates().add("badge");
+
+        try {
+            updateRole(roleMod);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
+        }
+
+        // 4. also add an actual attribute for badge - it will work        
+        roleMod.getPlainAttrsToUpdate().add(attrMod(badge.getKey(), "xxxxxxxxxx"));
+
+        roleTO = updateRole(roleMod);
+        assertNotNull(roleTO);
+        assertTrue(roleTO.getPlainAttrMap().containsKey(badge.getKey()));
+    }
+
+    @Test
+    public void anonymous() {
+        RoleService unauthenticated = clientFactory.createAnonymous().getService(RoleService.class);
+        try {
+            unauthenticated.list();
+            fail();
+        } catch (AccessControlException e) {
+            assertNotNull(e);
+        }
+
+        RoleService anonymous = clientFactory.create(ANONYMOUS_UNAME, ANONYMOUS_KEY).getService(RoleService.class);
+        assertFalse(anonymous.list().getResult().isEmpty());
+    }
+
+    @Test
+    public void noContent() throws IOException {
+        SyncopeClient noContentclient = clientFactory.create(ADMIN_UNAME, ADMIN_PWD);
+        RoleService noContentService = noContentclient.prefer(RoleService.class, Preference.RETURN_NO_CONTENT);
+
+        RoleTO role = buildRoleTO("noContent");
+
+        Response response = noContentService.create(role);
+        assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
+        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
+        assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
+
+        role = getObject(response.getLocation(), RoleService.class, RoleTO.class);
+        assertNotNull(role);
+
+        RoleMod roleMod = new RoleMod();
+        roleMod.getPlainAttrsToUpdate().add(attrMod("badge", "xxxxxxxxxx"));
+
+        response = noContentService.update(role.getKey(), roleMod);
+        assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
+        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
+        assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
+
+        response = noContentService.delete(role.getKey());
+        assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
+        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
+        assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
+    }
+
+    @Test
+    public void issueSYNCOPE455() {
+        final String parentName = "issueSYNCOPE455-PRole";
+        final String childName = "issueSYNCOPE455-CRole";
+
+        // 1. create parent role
+        RoleTO parent = buildBasicRoleTO(parentName);
+        parent.getResources().add(RESOURCE_NAME_LDAP);
+
+        parent = createRole(parent);
+        assertTrue(parent.getResources().contains(RESOURCE_NAME_LDAP));
+
+        final ConnObjectTO parentRemoteObject =
+                resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, parent.getKey());
+        assertNotNull(parentRemoteObject);
+        assertNotNull(getLdapRemoteObject(parentRemoteObject.getPlainAttrMap().get(Name.NAME).getValues().get(0)));
+
+        // 2. create child role
+        RoleTO child = buildBasicRoleTO(childName);
+        child.getResources().add(RESOURCE_NAME_LDAP);
+        child.setParent(parent.getKey());
+
+        child = createRole(child);
+        assertTrue(child.getResources().contains(RESOURCE_NAME_LDAP));
+
+        final ConnObjectTO childRemoteObject =
+                resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, child.getKey());
+        assertNotNull(childRemoteObject);
+        assertNotNull(getLdapRemoteObject(childRemoteObject.getPlainAttrMap().get(Name.NAME).getValues().get(0)));
+
+        // 3. remove parent role
+        roleService.delete(parent.getKey());
+
+        // 4. asserts for issue 455
+        try {
+            roleService.read(parent.getKey());
+            fail();
+        } catch (SyncopeClientException scce) {
+            assertNotNull(scce);
+        }
+
+        try {
+            roleService.read(child.getKey());
+            fail();
+        } catch (SyncopeClientException scce) {
+            assertNotNull(scce);
+        }
+
+        assertNull(getLdapRemoteObject(parentRemoteObject.getPlainAttrMap().get(Name.NAME).getValues().get(0)));
+        assertNull(getLdapRemoteObject(childRemoteObject.getPlainAttrMap().get(Name.NAME).getValues().get(0)));
+    }
+
+    @Test
+    public void issueSYNCOPE543() {
+        final String ancestorName = "issueSYNCOPE543-ARole";
+        final String parentName = "issueSYNCOPE543-PRole";
+        final String childName = "issueSYNCOPE543-CRole";
+
+        // 1. create ancestor role
+        RoleTO ancestor = buildBasicRoleTO(ancestorName);
+        ancestor.setParent(0L);
+        ancestor.getRAttrTemplates().add("icon");
+        ancestor.getPlainAttrs().add(attrTO("icon", "ancestorIcon"));
+        ancestor = createRole(ancestor);
+        assertEquals("ancestorIcon", ancestor.getPlainAttrMap().get("icon").getValues().get(0));
+
+        // 2. create parent role
+        RoleTO parent = buildBasicRoleTO(parentName);
+        parent.setParent(ancestor.getKey());
+        parent.getRAttrTemplates().add("icon");
+        parent.getPlainAttrs().add(attrTO("icon", "parentIcon"));
+        parent = createRole(parent);
+        assertEquals("parentIcon", parent.getPlainAttrMap().get("icon").getValues().get(0));
+
+        // 3. create child role
+        RoleTO child = buildBasicRoleTO(childName);
+        child.setParent(parent.getKey());
+        child.getRAttrTemplates().add("icon");
+        child.getPlainAttrs().add(attrTO("icon", "childIcon"));
+        child = createRole(child);
+        assertEquals("childIcon", child.getPlainAttrMap().get("icon").getValues().get(0));
+
+        final RoleMod roleChildMod = new RoleMod();
+        roleChildMod.setKey(child.getKey());
+        roleChildMod.setInheritPlainAttrs(Boolean.TRUE);
+        updateRole(roleChildMod);
+
+        child = roleService.read(child.getKey());
+        assertNotNull(child);
+        assertNotNull(child.getPlainAttrMap().get("icon").getValues());
+        assertEquals("parentIcon", child.getPlainAttrMap().get("icon").getValues().get(0));
+
+        final RoleMod roleParentMod = new RoleMod();
+        roleParentMod.setKey(parent.getKey());
+        roleParentMod.setInheritPlainAttrs(Boolean.TRUE);
+        updateRole(roleParentMod);
+
+        child = roleService.read(child.getKey());
+        assertNotNull(child);
+        assertNotNull(child.getPlainAttrMap().get("icon").getValues());
+        assertEquals("ancestorIcon", child.getPlainAttrMap().get("icon").getValues().get(0));
+
+        parent = roleService.read(parent.getKey());
+        assertNotNull(parent);
+        assertNotNull(parent.getPlainAttrMap().get("icon").getValues());
+        assertEquals("ancestorIcon", parent.getPlainAttrMap().get("icon").getValues().get(0));
+
+        roleParentMod.setInheritPlainAttrs(Boolean.FALSE);
+        updateRole(roleParentMod);
+
+        child = roleService.read(child.getKey());
+        assertNotNull(child);
+        assertNotNull(child.getPlainAttrMap().get("icon").getValues());
+        assertEquals("parentIcon", child.getPlainAttrMap().get("icon").getValues().get(0));
+    }
+}