You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by fm...@apache.org on 2013/10/07 16:38:57 UTC

svn commit: r1529856 [3/3] - in /syncope/trunk: common/src/main/java/org/apache/syncope/common/services/ common/src/main/java/org/apache/syncope/common/to/ console/ console/src/main/java/org/apache/syncope/console/commons/ console/src/main/java/org/apa...

Modified: syncope/trunk/console/src/test/java/org/apache/syncope/console/ResourceTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/console/src/test/java/org/apache/syncope/console/ResourceTestITCase.java?rev=1529856&r1=1529855&r2=1529856&view=diff
==============================================================================
--- syncope/trunk/console/src/test/java/org/apache/syncope/console/ResourceTestITCase.java (original)
+++ syncope/trunk/console/src/test/java/org/apache/syncope/console/ResourceTestITCase.java Mon Oct  7 14:38:55 2013
@@ -42,7 +42,7 @@ public class ResourceTestITCase extends 
 
         selenium.waitForCondition("selenium.isElementPresent(\"//div[@id='tabs']\");", "30000");
 
-        selenium.click("//td[6]/div/span[9]/a");
+        selenium.click("//td[6]/div/span[12]/a");
 
         selenium.waitForCondition("selenium.isElementPresent("
                 + "\"//form/div[3]/div/span/div/div/div/label[text()='Name']\");", "30000");
@@ -64,7 +64,7 @@ public class ResourceTestITCase extends 
 
         selenium.waitForCondition("selenium.isElementPresent(\"//div[@id='tabs']\");", "30000");
 
-        selenium.click("//tr[3]/td[6]/div/span[11]/a");
+        selenium.click("//tr[3]/td[6]/div/span[14]/a");
 
         assertTrue(selenium.getConfirmation().matches("^Do you really want to delete the selected item[\\s\\S]$"));
     }
@@ -75,7 +75,7 @@ public class ResourceTestITCase extends 
 
         selenium.waitForCondition("selenium.isElementPresent(\"//div[@id='tabs']\");", "30000");
 
-        selenium.click("//td[6]/div/span[9]/a");
+        selenium.click("//td[6]/div/span[12]/a");
 
         selenium.waitForCondition("selenium.isElementPresent("
                 + "\"//form/div[3]/div/span/div/div/div/label[text()='Name']\");", "30000");
@@ -97,7 +97,7 @@ public class ResourceTestITCase extends 
 
         selenium.waitForCondition("selenium.isElementPresent(\"//div[@id='tabs']\");", "30000");
 
-        selenium.click("//*[@id=\"users-contain\"]//*[div=\"ws-target-resource-delete\"]/../td[6]/div/span[9]/a");
+        selenium.click("//*[@id=\"users-contain\"]//*[div=\"ws-target-resource-delete\"]/../td[6]/div/span[12]/a");
 
         selenium.waitForCondition("selenium.isElementPresent("
                 + "\"//form/div[3]/div/span/div/div/div/label[text()='Name']\");", "30000");

Modified: syncope/trunk/console/src/test/java/org/apache/syncope/console/RoleTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/console/src/test/java/org/apache/syncope/console/RoleTestITCase.java?rev=1529856&r1=1529855&r2=1529856&view=diff
==============================================================================
--- syncope/trunk/console/src/test/java/org/apache/syncope/console/RoleTestITCase.java (original)
+++ syncope/trunk/console/src/test/java/org/apache/syncope/console/RoleTestITCase.java Mon Oct  7 14:38:55 2013
@@ -130,9 +130,9 @@ public class RoleTestITCase extends Abst
 
         selenium.click("//input[@name=\"userListContainer:search\"]");
 
-        selenium.waitForCondition("selenium.isElementPresent(\"//table/tbody/tr/td[5]/div/span[9]/a\");", "30000");
+        selenium.waitForCondition("selenium.isElementPresent(\"//table/tbody/tr/td[5]/div/span[12]/a\");", "30000");
 
-        selenium.click("//table/tbody/tr/td[5]/div/span[9]/a");
+        selenium.click("//table/tbody/tr/td[5]/div/span[12]/a");
 
         selenium.waitForCondition("selenium.isElementPresent("
                 + "\"//form/div[3]/div/span/div/div/div[contains(text(),'Username')]\");", "30000");
@@ -156,9 +156,9 @@ public class RoleTestITCase extends Abst
 
         selenium.click("//input[@name=\"userListContainer:search\"]");
 
-        selenium.waitForCondition("selenium.isElementPresent(\"//span[11]/a\");", "30000");
+        selenium.waitForCondition("selenium.isElementPresent(\"//span[14]/a\");", "30000");
 
-        selenium.click("//span[11]/a");
+        selenium.click("//span[14]/a");
 
         assertTrue(selenium.getConfirmation().matches("^Do you really want to delete the selected item[\\s\\S]$"));
     }

Modified: syncope/trunk/console/src/test/java/org/apache/syncope/console/SchemaTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/console/src/test/java/org/apache/syncope/console/SchemaTestITCase.java?rev=1529856&r1=1529855&r2=1529856&view=diff
==============================================================================
--- syncope/trunk/console/src/test/java/org/apache/syncope/console/SchemaTestITCase.java (original)
+++ syncope/trunk/console/src/test/java/org/apache/syncope/console/SchemaTestITCase.java Mon Oct  7 14:38:55 2013
@@ -48,7 +48,7 @@ public class SchemaTestITCase extends Ab
 
         selenium.waitForCondition("selenium.isElementPresent(\"//div[@id='tabs']\");", "30000");
 
-        selenium.click("//div[3]/div/div/div/div/div/span/table/tbody/tr/td[7]/div/span[11]/a");
+        selenium.click("//div[3]/div/div/div/div/div/span/table/tbody/tr/td[7]/div/span[14]/a");
 
         assertTrue(selenium.getConfirmation().matches("^Do you really want to delete the selected item[\\s\\S]$"));
     }

Modified: syncope/trunk/console/src/test/java/org/apache/syncope/console/TaskTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/console/src/test/java/org/apache/syncope/console/TaskTestITCase.java?rev=1529856&r1=1529855&r2=1529856&view=diff
==============================================================================
--- syncope/trunk/console/src/test/java/org/apache/syncope/console/TaskTestITCase.java (original)
+++ syncope/trunk/console/src/test/java/org/apache/syncope/console/TaskTestITCase.java Mon Oct  7 14:38:55 2013
@@ -29,11 +29,11 @@ public class TaskTestITCase extends Abst
         selenium.waitForCondition("selenium.isElementPresent(\"//div[@id='tabs']\");", "30000");
 
         selenium.click("//div[@id='tabs']/ul/li[4]/a");
-        selenium.click("//*[div=1]/../td[10]/div/span[3]/a");
+        selenium.click("//*[div=1]/../td[10]/div/span[6]/a");
 
         selenium.waitForCondition("selenium.isTextPresent(" + "\"Operation executed successfully\");", "30000");
 
-        selenium.click("//*[div=1]/../td[10]/div/span[9]/a");
+        selenium.click("//*[div=1]/../td[10]/div/span[12]/a");
 
         selenium.waitForCondition("selenium.isElementPresent("
                 + "\"//form/div[2]/div/div/span/div/div/div[2]/span/input\");", "30000");
@@ -50,7 +50,7 @@ public class TaskTestITCase extends Abst
         selenium.waitForCondition("selenium.isElementPresent(\"//div[@id='tabs']\");", "30000");
 
         selenium.click("//div[@id='tabs']/ul/li[3]/a/span");
-        selenium.click("//table/tbody/tr/td[8]/div/span[11]/a");
+        selenium.click("//table/tbody/tr/td[8]/div/span[14]/a");
 
         assertTrue(selenium.getConfirmation().matches("^Do you really want to delete the selected item[\\s\\S]$"));
 

Modified: syncope/trunk/console/src/test/java/org/apache/syncope/console/UserTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/console/src/test/java/org/apache/syncope/console/UserTestITCase.java?rev=1529856&r1=1529855&r2=1529856&view=diff
==============================================================================
--- syncope/trunk/console/src/test/java/org/apache/syncope/console/UserTestITCase.java (original)
+++ syncope/trunk/console/src/test/java/org/apache/syncope/console/UserTestITCase.java Mon Oct  7 14:38:55 2013
@@ -50,7 +50,7 @@ public class UserTestITCase extends Abst
         selenium.waitForCondition("selenium.isElementPresent(\"//div[@id='tabs']\");", "30000");
 
         //Edit vivaldi
-        selenium.click("//*[@id=\"users-contain\"]//*[div=3]/../td[5]/div/span[9]/a");
+        selenium.click("//*[@id=\"users-contain\"]//*[div=3]/../td[5]/div/span[12]/a");
 
         selenium.waitForCondition("selenium.isElementPresent(" + "\"//input[@value='Antonio Vivaldi']\");", "30000");
 
@@ -89,7 +89,7 @@ public class UserTestITCase extends Abst
 
         selenium.waitForCondition("selenium.isElementPresent(\"//div[@id='tabs']\");", "30000");
 
-        selenium.click("//*[@id=\"users-contain\"]//*[div=4]/../td[5]/div/span[11]/a");
+        selenium.click("//*[@id=\"users-contain\"]//*[div=4]/../td[5]/div/span[14]/a");
 
         assertTrue(selenium.getConfirmation().matches("^Do you really want to delete the selected item[\\s\\S]$"));
 

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java?rev=1529856&r1=1529855&r2=1529856&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java Mon Oct  7 14:38:55 2013
@@ -20,6 +20,7 @@ package org.apache.syncope.core.propagat
 
 import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -362,7 +363,7 @@ public class PropagationManager {
     public List<PropagationTask> getUserDeleteTaskIds(final Long userId)
             throws NotFoundException, UnauthorizedRoleException {
 
-        return getUserDeleteTaskIds(userId, null);
+        return getUserDeleteTaskIds(userId, Collections.<String>emptySet());
     }
 
     /**
@@ -371,12 +372,30 @@ public class PropagationManager {
      * the creation fails onto a mandatory resource.
      *
      * @param userId to be deleted
-     * @param noPropResourceNames name of external resource not to be considered for propagation
+     * @param noPropResourceName name of external resource not to be considered for propagation
+     * @return list of propagation tasks
+     * @throws NotFoundException if user is not found
+     * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given user
+     */
+    public List<PropagationTask> getUserDeleteTaskIds(final Long userId, final String noPropResourceName)
+            throws NotFoundException, UnauthorizedRoleException {
+
+        SyncopeUser user = userDataBinder.getUserFromId(userId);
+        return getDeleteTaskIds(user, Collections.<String>singleton(noPropResourceName));
+    }
+
+    /**
+     * Perform delete on each resource associated to the user. It is possible to ask for a mandatory provisioning for
+     * some resources specifying a set of resource names. Exceptions won't be ignored and the process will be stopped if
+     * the creation fails onto a mandatory resource.
+     *
+     * @param userId to be deleted
+     * @param noPropResourceNames name of external resources not to be considered for propagation
      * @return list of propagation tasks
      * @throws NotFoundException if user is not found
      * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given user
      */
-    public List<PropagationTask> getUserDeleteTaskIds(final Long userId, final String noPropResourceNames)
+    public List<PropagationTask> getUserDeleteTaskIds(final Long userId, final Collection<String> noPropResourceNames)
             throws NotFoundException, UnauthorizedRoleException {
 
         SyncopeUser user = userDataBinder.getUserFromId(userId);
@@ -409,7 +428,7 @@ public class PropagationManager {
     public List<PropagationTask> getRoleDeleteTaskIds(final Long roleId)
             throws NotFoundException, UnauthorizedRoleException {
 
-        return getRoleDeleteTaskIds(roleId, null);
+        return getRoleDeleteTaskIds(roleId, Collections.<String>emptySet());
     }
 
     /**
@@ -426,17 +445,34 @@ public class PropagationManager {
     public List<PropagationTask> getRoleDeleteTaskIds(final Long roleId, final String noPropResourceName)
             throws NotFoundException, UnauthorizedRoleException {
 
+        return getRoleDeleteTaskIds(roleId, Collections.<String>singleton(noPropResourceName));
+    }
+
+    /**
+     * Perform delete on each resource associated to the user. It is possible to ask for a mandatory provisioning for
+     * some resources specifying a set of resource names. Exceptions won't be ignored and the process will be stopped if
+     * the creation fails onto a mandatory resource.
+     *
+     * @param roleId to be deleted
+     * @param noPropResourceNames name of external resources not to be considered for propagation
+     * @return list of propagation tasks
+     * @throws NotFoundException if role is not found
+     * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given role
+     */
+    public List<PropagationTask> getRoleDeleteTaskIds(final Long roleId, final Collection<String> noPropResourceNames)
+            throws NotFoundException, UnauthorizedRoleException {
+
         SyncopeRole role = roleDataBinder.getRoleFromId(roleId);
-        return getDeleteTaskIds(role, noPropResourceName);
+        return getDeleteTaskIds(role, noPropResourceNames);
     }
 
     protected List<PropagationTask> getDeleteTaskIds(final AbstractAttributable attributable,
-            final String noPropResourceName) {
+            final Collection<String> noPropResourceNames) {
 
         final PropagationByResource propByRes = new PropagationByResource();
         propByRes.set(ResourceOperation.DELETE, attributable.getResourceNames());
-        if (noPropResourceName != null) {
-            propByRes.get(ResourceOperation.DELETE).remove(noPropResourceName);
+        if (noPropResourceNames != null && !noPropResourceNames.isEmpty()) {
+            propByRes.get(ResourceOperation.DELETE).removeAll(noPropResourceNames);
         }
         return createTasks(attributable, null, null, null, false, true, propByRes);
     }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/ResourceController.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/ResourceController.java?rev=1529856&r1=1529855&r2=1529856&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/ResourceController.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/ResourceController.java Mon Oct  7 14:38:55 2013
@@ -18,6 +18,11 @@
  */
 package org.apache.syncope.core.rest.controller;
 
+import static org.apache.syncope.core.rest.controller.AbstractController.LOG;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 import javax.persistence.EntityExistsException;
@@ -25,6 +30,7 @@ import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.to.BulkAction;
 import org.apache.syncope.common.to.BulkActionRes;
+import org.apache.syncope.common.to.BulkAssociationAction;
 import org.apache.syncope.common.to.ConnObjectTO;
 import org.apache.syncope.common.to.ResourceTO;
 import org.apache.syncope.common.types.AttributableType;
@@ -88,6 +94,12 @@ public class ResourceController extends 
     @Autowired
     private ImplementationClassNamesLoader classNamesLoader;
 
+    @Autowired
+    private UserController userController;
+
+    @Autowired
+    private RoleController roleController;
+
     /**
      * ConnectorObject util.
      */
@@ -308,4 +320,50 @@ public class ResourceController extends 
 
         return res;
     }
+
+    public BulkActionRes usersBulkAssociationAction(
+            final String resourceName, final BulkAssociationAction bulkAction) {
+        return bulkAssociationActionDelegate(userController, bulkAction, resourceName);
+    }
+
+    public BulkActionRes rolesBulkAssociationAction(
+            final String resourceName, final BulkAssociationAction bulkAction) {
+        return bulkAssociationActionDelegate(roleController, bulkAction, resourceName);
+    }
+
+    private BulkActionRes bulkAssociationActionDelegate(
+            final Object obj,
+            final BulkAssociationAction bulkAction,
+            final String resourceName) {
+
+        final String methodName = bulkAction.getOperation().name().toLowerCase();
+
+        try {
+            final Method method = obj.getClass().getMethod(methodName, Long.class, Collection.class);
+
+            final BulkActionRes res = new BulkActionRes();
+
+            for (Long id : bulkAction.getTargets()) {
+                try {
+                    // audit is delegated to the called userController method ...
+                    method.invoke(obj, id, Collections.<String>singleton(resourceName));
+                    res.add(id, BulkActionRes.Status.SUCCESS);
+                } catch (Exception e) {
+                    LOG.warn("Error performing {} of user {}", methodName, id, e);
+                    res.add(id, BulkActionRes.Status.FAILURE);
+                }
+            }
+
+            return res;
+        } catch (Exception e) {
+            LOG.error("Error retrieving {} method", methodName, e);
+
+            SyncopeClientCompositeException scce =
+                    new SyncopeClientCompositeException(Response.Status.BAD_REQUEST.getStatusCode());
+            SyncopeClientException sce = new SyncopeClientException(SyncopeClientExceptionType.Unknown);
+            sce.addElement("Operation execution failed");
+            scce.addException(sce);
+            throw scce;
+        }
+    }
 }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java?rev=1529856&r1=1529855&r2=1529856&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java Mon Oct  7 14:38:55 2013
@@ -19,6 +19,7 @@
 package org.apache.syncope.core.rest.controller;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
@@ -349,4 +350,69 @@ public class RoleController {
 
         return roleTO;
     }
+
+    @PreAuthorize("hasRole('ROLE_UPDATE')")
+    @Transactional(rollbackFor = {Throwable.class})
+    public RoleTO unlink(final Long roleId, final Collection<String> resources) {
+        LOG.debug("About to unlink role({}) and resources {}", roleId, resources);
+
+        final RoleMod roleMod = new RoleMod();
+        roleMod.setId(roleId);
+
+        roleMod.getResourcesToBeRemoved().addAll(resources);
+
+        final WorkflowResult<Long> updated = rwfAdapter.update(roleMod);
+
+        final RoleTO updatedTO = binder.getRoleTO(updated.getResult());
+
+        auditManager.audit(Category.user, AuditElements.RoleSubCategory.update, Result.success,
+                "Successfully updated role: " + updatedTO.getName());
+
+        LOG.debug("About to return updated role\n{}", updatedTO);
+
+        return updatedTO;
+    }
+
+    @PreAuthorize("hasRole('ROLE_UPDATE')")
+    @Transactional(rollbackFor = {Throwable.class})
+    public RoleTO unassign(final Long roleId, final Collection<String> resources) {
+        LOG.debug("About to unassign role({}) and resources {}", roleId, resources);
+
+        final RoleMod roleMod = new RoleMod();
+        roleMod.setId(roleId);
+        roleMod.getResourcesToBeRemoved().addAll(resources);
+
+        return update(roleMod);
+    }
+
+    @PreAuthorize("hasRole('ROLE_UPDATE')")
+    @Transactional(rollbackFor = {Throwable.class})
+    public RoleTO deprovision(final Long roleId, final Collection<String> resources) {
+        LOG.debug("About to deprovision role({}) from resources {}", roleId, resources);
+
+        final SyncopeRole role = binder.getRoleFromId(roleId);
+
+        final Set<String> noPropResourceName = role.getResourceNames();
+        noPropResourceName.removeAll(resources);
+
+        final List<PropagationTask> tasks = propagationManager.getRoleDeleteTaskIds(roleId, noPropResourceName);
+        final List<PropagationStatusTO> propagations = new ArrayList<PropagationStatusTO>();
+        final DefaultPropagationHandler propHanlder = new DefaultPropagationHandler(connObjectUtil, propagations);
+        try {
+            taskExecutor.execute(tasks, propHanlder);
+        } catch (PropagationException e) {
+            LOG.error("Error propagation primary resource", e);
+            propHanlder.completeWhenPrimaryResourceErrored(propagations, tasks);
+        }
+
+        final RoleTO updatedTO = binder.getRoleTO(role);
+        updatedTO.getPropagationStatusTOs().addAll(propagations);
+
+        auditManager.audit(Category.user, AuditElements.RoleSubCategory.update, Result.success,
+                "Successfully deprovisioned role: " + updatedTO.getName());
+
+        LOG.debug("About to return updated role\n{}", updatedTO);
+
+        return updatedTO;
+    }
 }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java?rev=1529856&r1=1529855&r2=1529856&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java Mon Oct  7 14:38:55 2013
@@ -19,6 +19,7 @@
 package org.apache.syncope.core.rest.controller;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -478,7 +479,7 @@ public class UserController {
     protected UserTO setStatus(final SyncopeUser user, final String token,
             final PropagationRequestTO propagationRequestTO, final boolean status, final String task) {
 
-        LOG.debug("About to set status of {}" + user);
+        LOG.debug("About to set 'enabled:{}' status to {}", user, status);
 
         WorkflowResult<Long> updated;
         if (propagationRequestTO == null || propagationRequestTO.isOnSyncope()) {
@@ -619,4 +620,69 @@ public class UserController {
 
         return res;
     }
+
+    @PreAuthorize("hasRole('USER_UPDATE')")
+    @Transactional(rollbackFor = {Throwable.class})
+    public UserTO unlink(final Long userId, final Collection<String> resources) {
+        LOG.debug("About to unlink user({}) and resources {}", userId, resources);
+
+        final UserMod userMod = new UserMod();
+        userMod.setId(userId);
+
+        userMod.getResourcesToBeRemoved().addAll(resources);
+
+        final WorkflowResult<Map.Entry<Long, Boolean>> updated = uwfAdapter.update(userMod);
+
+        final UserTO updatedTO = binder.getUserTO(updated.getResult().getKey());
+
+        auditManager.audit(Category.user, UserSubCategory.update, Result.success,
+                "Successfully updated user: " + updatedTO.getUsername());
+
+        LOG.debug("About to return updated user\n{}", updatedTO);
+
+        return updatedTO;
+    }
+
+    @PreAuthorize("hasRole('USER_UPDATE')")
+    @Transactional(rollbackFor = {Throwable.class})
+    public UserTO unassign(final Long userId, final Collection<String> resources) {
+        LOG.debug("About to unassign user({}) and resources {}", userId, resources);
+
+        final UserMod userMod = new UserMod();
+        userMod.setId(userId);
+        userMod.getResourcesToBeRemoved().addAll(resources);
+
+        return update(userMod);
+    }
+
+    @PreAuthorize("hasRole('USER_UPDATE')")
+    @Transactional(rollbackFor = {Throwable.class})
+    public UserTO deprovision(final Long userId, final Collection<String> resources) {
+        LOG.debug("About to deprovision user({}) from resources {}", userId, resources);
+
+        final SyncopeUser user = binder.getUserFromId(userId);
+
+        final Set<String> noPropResourceName = user.getResourceNames();
+        noPropResourceName.removeAll(resources);
+
+        final List<PropagationTask> tasks = propagationManager.getUserDeleteTaskIds(userId, noPropResourceName);
+        final List<PropagationStatusTO> propagations = new ArrayList<PropagationStatusTO>();
+        final DefaultPropagationHandler propHanlder = new DefaultPropagationHandler(connObjectUtil, propagations);
+        try {
+            taskExecutor.execute(tasks, propHanlder);
+        } catch (PropagationException e) {
+            LOG.error("Error propagation primary resource", e);
+            propHanlder.completeWhenPrimaryResourceErrored(propagations, tasks);
+        }
+
+        final UserTO updatedUserTO = binder.getUserTO(user);
+        updatedUserTO.getPropagationStatusTOs().addAll(propagations);
+
+        auditManager.audit(Category.user, UserSubCategory.update, Result.success,
+                "Successfully deprovisioned user: " + updatedUserTO.getUsername());
+
+        LOG.debug("About to return updated user\n{}", updatedUserTO);
+
+        return updatedUserTO;
+    }
 }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/services/ResourceServiceImpl.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/services/ResourceServiceImpl.java?rev=1529856&r1=1529855&r2=1529856&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/services/ResourceServiceImpl.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/services/ResourceServiceImpl.java Mon Oct  7 14:38:55 2013
@@ -28,6 +28,7 @@ import org.apache.syncope.common.Syncope
 import org.apache.syncope.common.services.ResourceService;
 import org.apache.syncope.common.to.BulkAction;
 import org.apache.syncope.common.to.BulkActionRes;
+import org.apache.syncope.common.to.BulkAssociationAction;
 import org.apache.syncope.common.to.ConnObjectTO;
 import org.apache.syncope.common.to.PropagationActionClassTO;
 import org.apache.syncope.common.to.ResourceTO;
@@ -96,4 +97,14 @@ public class ResourceServiceImpl extends
     public BulkActionRes bulkAction(final BulkAction bulkAction) {
         return controller.bulkAction(bulkAction);
     }
+
+    @Override
+    public BulkActionRes usersBulkAssociationAction(final String resourceName, final BulkAssociationAction bulkAction) {
+        return controller.usersBulkAssociationAction(resourceName, bulkAction);
+    }
+
+    @Override
+    public BulkActionRes rolesBulkAssociationAction(final String resourceName, final BulkAssociationAction bulkAction) {
+        return controller.rolesBulkAssociationAction(resourceName, bulkAction);
+    }
 }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java?rev=1529856&r1=1529855&r2=1529856&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java Mon Oct  7 14:38:55 2013
@@ -29,6 +29,7 @@ import org.apache.syncope.common.mod.Rol
 import org.apache.syncope.common.search.NodeCond;
 import org.apache.syncope.common.services.InvalidSearchConditionException;
 import org.apache.syncope.common.services.RoleService;
+import org.apache.syncope.common.to.PropagationTargetsTO;
 import org.apache.syncope.common.to.RoleTO;
 import org.apache.syncope.core.rest.controller.RoleController;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -111,4 +112,19 @@ public class RoleServiceImpl extends Abs
     public RoleTO update(final Long roleId, final RoleMod roleMod) {
         return controller.update(roleMod);
     }
+
+    @Override
+    public RoleTO unlink(final Long roleId, final PropagationTargetsTO propagationTargetsTO) {
+        return controller.unlink(roleId, propagationTargetsTO.getResources());
+    }
+
+    @Override
+    public RoleTO unassign(final Long roleId, final PropagationTargetsTO propagationTargetsTO) {
+        return controller.unassign(roleId, propagationTargetsTO.getResources());
+    }
+
+    @Override
+    public RoleTO deprovision(final Long roleId, final PropagationTargetsTO propagationTargetsTO) {
+        return controller.deprovision(roleId, propagationTargetsTO.getResources());
+    }
 }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java?rev=1529856&r1=1529855&r2=1529856&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java Mon Oct  7 14:38:55 2013
@@ -29,6 +29,7 @@ import org.apache.syncope.common.service
 import org.apache.syncope.common.to.BulkAction;
 import org.apache.syncope.common.to.BulkActionRes;
 import org.apache.syncope.common.to.PropagationRequestTO;
+import org.apache.syncope.common.to.PropagationTargetsTO;
 import org.apache.syncope.common.to.UserTO;
 import org.apache.syncope.core.rest.controller.UserController;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -171,4 +172,19 @@ public class UserServiceImpl extends Abs
     public BulkActionRes bulkAction(final BulkAction bulkAction) {
         return controller.bulkAction(bulkAction);
     }
+
+    @Override
+    public UserTO unlink(final Long userId, final PropagationTargetsTO propagationTargetsTO) {
+        return controller.unlink(userId, propagationTargetsTO.getResources());
+    }
+
+    @Override
+    public UserTO unassign(final Long userId, final PropagationTargetsTO propagationTargetsTO) {
+        return controller.unassign(userId, propagationTargetsTO.getResources());
+    }
+
+    @Override
+    public UserTO deprovision(final Long userId, final PropagationTargetsTO propagationTargetsTO) {
+        return controller.deprovision(userId, propagationTargetsTO.getResources());
+    }
 }

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java?rev=1529856&r1=1529855&r2=1529856&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java Mon Oct  7 14:38:55 2013
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.core.rest;
 
+import static org.apache.syncope.core.rest.AbstractTest.attributeTO;
+import static org.apache.syncope.core.rest.AbstractTest.roleService;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -32,6 +34,7 @@ import org.apache.http.HttpStatus;
 import org.apache.syncope.common.mod.RoleMod;
 import org.apache.syncope.common.services.RoleService;
 import org.apache.syncope.common.to.ConnObjectTO;
+import org.apache.syncope.common.to.PropagationTargetsTO;
 import org.apache.syncope.common.to.RoleTO;
 import org.apache.syncope.common.to.UserTO;
 import org.apache.syncope.common.types.AttributableType;
@@ -388,4 +391,78 @@ public class RoleTestITCase extends Abst
         assertNotNull(roleTO);
         assertTrue(roleTO.getEntitlements().isEmpty());
     }
+
+    @Test
+    public void unlink() {
+        RoleTO actual = createRole(roleService, buildRoleTO("unlink"));
+        assertNotNull(actual);
+
+        assertNotNull(readConnectorObject("resource-ldap", actual.getId()));
+
+        PropagationTargetsTO res = new PropagationTargetsTO();
+        res.getResources().add("resource-ldap");
+
+        actual = roleService.unlink(actual.getId(), res);
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+
+        actual = roleService.read(actual.getId());
+        assertNotNull(actual);
+
+        assertTrue(actual.getResources().isEmpty());
+
+        assertNotNull(readConnectorObject("resource-ldap", actual.getId()));
+    }
+
+    @Test
+    public void unassign() {
+        RoleTO actual = createRole(roleService, buildRoleTO("unassign"));
+        assertNotNull(actual);
+
+        assertNotNull(readConnectorObject("resource-ldap", actual.getId()));
+
+        PropagationTargetsTO res = new PropagationTargetsTO();
+        res.getResources().add("resource-ldap");
+
+        actual = roleService.unassign(actual.getId(), res);
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+
+        actual = roleService.read(actual.getId());
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+
+        try {
+            readConnectorObject("resource-ldap", actual.getId());
+            fail();
+        } catch (Exception e) {
+            // ignore
+        }
+    }
+
+    @Test
+    public void deprovision() {
+        RoleTO actual = createRole(roleService, buildRoleTO("deprovision"));
+        assertNotNull(actual);
+
+        assertNotNull(readConnectorObject("resource-ldap", actual.getId()));
+
+        PropagationTargetsTO res = new PropagationTargetsTO();
+        res.getResources().add("resource-ldap");
+
+        actual = roleService.deprovision(actual.getId(), res);
+        assertNotNull(actual);
+        assertFalse(actual.getResources().isEmpty());
+
+        actual = roleService.read(actual.getId());
+        assertNotNull(actual);
+        assertFalse(actual.getResources().isEmpty());
+
+        try {
+            readConnectorObject("resource-ldap", actual.getId());
+            fail();
+        } catch (Exception e) {
+            // ignore
+        }
+    }
 }

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java?rev=1529856&r1=1529855&r2=1529856&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java Mon Oct  7 14:38:55 2013
@@ -22,6 +22,7 @@ import static org.apache.syncope.core.re
 import static org.apache.syncope.core.rest.AbstractTest.attributeMod;
 import static org.apache.syncope.core.rest.AbstractTest.attributeTO;
 import static org.apache.syncope.core.rest.AbstractTest.getUUIDString;
+import static org.apache.syncope.core.rest.AbstractTest.userService;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -60,6 +61,7 @@ import org.apache.syncope.common.to.Memb
 import org.apache.syncope.common.to.PasswordPolicyTO;
 import org.apache.syncope.common.to.PropagationRequestTO;
 import org.apache.syncope.common.to.PropagationStatusTO;
+import org.apache.syncope.common.to.PropagationTargetsTO;
 import org.apache.syncope.common.to.PropagationTaskTO;
 import org.apache.syncope.common.to.ResourceTO;
 import org.apache.syncope.common.to.RoleTO;
@@ -2419,6 +2421,108 @@ public class UserTestITCase extends Abst
         }
     }
 
+    @Test
+    public void unlink() {
+        UserTO userTO = getUniqueSampleTO("unlink@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getDerivedAttributes().clear();
+        userTO.getVirtualAttributes().clear();
+        userTO.getDerivedAttributes().add(attributeTO("csvuserid", null));
+        userTO.getResources().add(RESOURCE_NAME_CSV);
+
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+
+        ConnObjectTO connObjectTO = readConnectorObject(RESOURCE_NAME_CSV, actual.getId());
+        assertNotNull(connObjectTO);
+
+        PropagationTargetsTO res = new PropagationTargetsTO();
+        res.getResources().add(RESOURCE_NAME_CSV);
+
+        actual = userService.unlink(actual.getId(), res);
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+
+        actual = userService.read(actual.getId());
+        assertNotNull(actual);
+
+        assertTrue(actual.getResources().isEmpty());
+
+        connObjectTO = readConnectorObject(RESOURCE_NAME_CSV, actual.getId());
+        assertNotNull(connObjectTO);
+    }
+
+    @Test
+    public void unassign() {
+        UserTO userTO = getUniqueSampleTO("unassign@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getDerivedAttributes().clear();
+        userTO.getVirtualAttributes().clear();
+        userTO.getDerivedAttributes().add(attributeTO("csvuserid", null));
+        userTO.getResources().add(RESOURCE_NAME_CSV);
+
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+
+        ConnObjectTO connObjectTO = readConnectorObject(RESOURCE_NAME_CSV, actual.getId());
+        assertNotNull(connObjectTO);
+
+        PropagationTargetsTO res = new PropagationTargetsTO();
+        res.getResources().add(RESOURCE_NAME_CSV);
+
+        actual = userService.unassign(actual.getId(), res);
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+
+        actual = userService.read(actual.getId());
+        assertNotNull(actual);
+        assertTrue(actual.getResources().isEmpty());
+
+        try {
+            readConnectorObject(RESOURCE_NAME_CSV, actual.getId());
+            fail();
+        } catch (Exception e) {
+            // ignore
+        }
+    }
+
+    @Test
+    public void deprovision() {
+        UserTO userTO = getUniqueSampleTO("deprovision@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        userTO.getDerivedAttributes().clear();
+        userTO.getVirtualAttributes().clear();
+        userTO.getDerivedAttributes().add(attributeTO("csvuserid", null));
+        userTO.getResources().add(RESOURCE_NAME_CSV);
+
+        UserTO actual = createUser(userTO);
+        assertNotNull(actual);
+
+        ConnObjectTO connObjectTO = readConnectorObject(RESOURCE_NAME_CSV, actual.getId());
+        assertNotNull(connObjectTO);
+
+        PropagationTargetsTO res = new PropagationTargetsTO();
+        res.getResources().add(RESOURCE_NAME_CSV);
+
+        actual = userService.deprovision(actual.getId(), res);
+        assertNotNull(actual);
+        assertFalse(actual.getResources().isEmpty());
+
+        actual = userService.read(actual.getId());
+        assertNotNull(actual);
+        assertFalse(actual.getResources().isEmpty());
+
+        try {
+            readConnectorObject(RESOURCE_NAME_CSV, actual.getId());
+            fail();
+        } catch (Exception e) {
+            // ignore
+        }
+    }
+
     private boolean getBooleanAttribute(ConnObjectTO connObjectTO, String attrName) {
         return Boolean.parseBoolean(getStringAttribute(connObjectTO, attrName));
     }