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/07/07 09:37:55 UTC

[1/7] syncope git commit: [SYNCOPE-676] Various improvements including a new optional 'details' boolean parameter for list and search, defaulting to true to keep backward compatibility

Repository: syncope
Updated Branches:
  refs/heads/1_2_X e1c258763 -> 261233722
  refs/heads/master 3eb0bfb6c -> 97607b16e


[SYNCOPE-676] Various improvements including a new optional 'details' boolean parameter for list and search, defaulting to true to keep backward compatibility


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

Branch: refs/heads/1_2_X
Commit: 2612337228f1fbd3818f61c4dd3371b7bf1d37fb
Parents: e1c2587
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon Jul 6 15:58:37 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon Jul 6 15:58:37 2015 +0200

----------------------------------------------------------------------
 .../syncope/common/services/JAXRSService.java   |  2 +
 .../syncope/common/services/RoleService.java    |  8 +-
 .../syncope/common/services/UserService.java    |  8 +-
 .../syncope/console/commons/Constants.java      |  2 -
 .../pages/DisplayAttributesModalPage.java       | 46 +----------
 .../pages/panels/UserSearchResultPanel.java     | 13 +---
 .../syncope/console/rest/RoleRestClient.java    | 12 ++-
 .../syncope/console/rest/UserRestClient.java    |  4 +-
 .../pages/DisplayAttributesModalPage.html       |  2 -
 .../syncope/console/ReportTestITCase.java       |  4 +-
 .../core/notification/NotificationManager.java  | 10 +--
 .../syncope/core/report/RoleReportlet.java      |  2 +-
 .../syncope/core/report/UserReportlet.java      |  4 +-
 .../controller/AbstractSubjectController.java   |  5 +-
 .../core/rest/controller/RoleController.java    | 22 +++---
 .../core/rest/controller/UserController.java    | 12 +--
 .../syncope/core/rest/data/RoleDataBinder.java  | 80 ++++++++++----------
 .../syncope/core/rest/data/UserDataBinder.java  | 51 +++++++------
 .../syncope/core/services/RoleServiceImpl.java  | 24 +++---
 .../syncope/core/services/UserServiceImpl.java  | 24 +++---
 .../core/sync/impl/RolePushResultHandler.java   |  4 +-
 .../activiti/ActivitiUserWorkflowAdapter.java   |  4 +-
 core/src/main/resources/indexes.xml             | 28 ++++++-
 23 files changed, 189 insertions(+), 182 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/common/src/main/java/org/apache/syncope/common/services/JAXRSService.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/syncope/common/services/JAXRSService.java b/common/src/main/java/org/apache/syncope/common/services/JAXRSService.java
index c801bb1..757d298 100644
--- a/common/src/main/java/org/apache/syncope/common/services/JAXRSService.java
+++ b/common/src/main/java/org/apache/syncope/common/services/JAXRSService.java
@@ -36,4 +36,6 @@ public interface JAXRSService {
 
     final String PARAM_ORDERBY = "orderby";
 
+    final String PARAM_DETAILS = "details";
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/common/src/main/java/org/apache/syncope/common/services/RoleService.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/syncope/common/services/RoleService.java b/common/src/main/java/org/apache/syncope/common/services/RoleService.java
index 1f4e45b..9bd1588 100644
--- a/common/src/main/java/org/apache/syncope/common/services/RoleService.java
+++ b/common/src/main/java/org/apache/syncope/common/services/RoleService.java
@@ -138,6 +138,7 @@ public interface RoleService extends JAXRSService {
      * @param page result page number
      * @param size number of entries per page
      * @param orderBy list of ordering clauses, separated by comma
+     * @param details whether include all details or not, defaults to true
      * @return paged list of existing roles matching page/size conditions
      */
     @GET
@@ -145,7 +146,8 @@ public interface RoleService extends JAXRSService {
     PagedResult<RoleTO> list(
             @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
             @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size,
-            @QueryParam(PARAM_ORDERBY) String orderBy);
+            @QueryParam(PARAM_ORDERBY) String orderBy,
+            @QueryParam(PARAM_DETAILS) @DefaultValue("true") boolean details);
 
     /**
      * Returns a paged list of roles matching the provided FIQL search condition.
@@ -193,6 +195,7 @@ public interface RoleService extends JAXRSService {
      * @param page result page number
      * @param size number of entries per page
      * @param orderBy list of ordering clauses, separated by comma
+     * @param details whether include all details or not, defaults to true
      * @return paged list of roles matching the provided FIQL search condition
      */
     @GET
@@ -201,7 +204,8 @@ public interface RoleService extends JAXRSService {
     PagedResult<RoleTO> search(@QueryParam(PARAM_FIQL) String fiql,
             @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
             @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size,
-            @QueryParam(PARAM_ORDERBY) String orderBy);
+            @QueryParam(PARAM_ORDERBY) String orderBy,
+            @QueryParam(PARAM_DETAILS) @DefaultValue("true") boolean details);
 
     /**
      * Creates a new role.

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/common/src/main/java/org/apache/syncope/common/services/UserService.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/syncope/common/services/UserService.java b/common/src/main/java/org/apache/syncope/common/services/UserService.java
index c720218..270920f 100644
--- a/common/src/main/java/org/apache/syncope/common/services/UserService.java
+++ b/common/src/main/java/org/apache/syncope/common/services/UserService.java
@@ -131,6 +131,7 @@ public interface UserService extends JAXRSService {
      * @param page result page number
      * @param size number of entries per page
      * @param orderBy list of ordering clauses, separated by comma
+     * @param details whether include all details or not, defaults to true
      * @return paged list of existing users matching page/size conditions
      */
     @GET
@@ -138,7 +139,8 @@ public interface UserService extends JAXRSService {
     PagedResult<UserTO> list(
             @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
             @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size,
-            @QueryParam(PARAM_ORDERBY) String orderBy);
+            @QueryParam(PARAM_ORDERBY) String orderBy,
+            @QueryParam(PARAM_DETAILS) @DefaultValue("true") boolean details);
 
     /**
      * Returns a paged list of users matching the provided FIQL search condition.
@@ -185,6 +187,7 @@ public interface UserService extends JAXRSService {
      * @param page result page number
      * @param size number of entries per page
      * @param orderBy list of ordering clauses, separated by comma
+     * @param details whether include all details or not, defaults to true
      * @return paged list of users matching the provided FIQL search condition
      */
     @GET
@@ -193,7 +196,8 @@ public interface UserService extends JAXRSService {
     PagedResult<UserTO> search(@QueryParam(PARAM_FIQL) String fiql,
             @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
             @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size,
-            @QueryParam(PARAM_ORDERBY) String orderBy);
+            @QueryParam(PARAM_ORDERBY) String orderBy,
+            @QueryParam(PARAM_DETAILS) @DefaultValue("true") boolean details);
 
     /**
      * Creates a new user.

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/console/src/main/java/org/apache/syncope/console/commons/Constants.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/commons/Constants.java b/console/src/main/java/org/apache/syncope/console/commons/Constants.java
index 92f8d77..d43759d 100644
--- a/console/src/main/java/org/apache/syncope/console/commons/Constants.java
+++ b/console/src/main/java/org/apache/syncope/console/commons/Constants.java
@@ -46,8 +46,6 @@ public final class Constants {
 
     public static final String PREF_USERS_DERIVED_ATTRIBUTES_VIEW = "users.derived.attributes.view";
 
-    public static final String PREF_USERS_VIRTUAL_ATTRIBUTES_VIEW = "users.virtual.attributes.view";
-
     public static final String PREF_CONF_SCHEMA_PAGINATOR_ROWS = "conf.schema.paginator.rows";
 
     public static final String PREF_USER_SCHEMA_PAGINATOR_ROWS = "user.schema.paginator.rows";

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/console/src/main/java/org/apache/syncope/console/pages/DisplayAttributesModalPage.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/pages/DisplayAttributesModalPage.java b/console/src/main/java/org/apache/syncope/console/pages/DisplayAttributesModalPage.java
index 724b7cd..dd60881 100644
--- a/console/src/main/java/org/apache/syncope/console/pages/DisplayAttributesModalPage.java
+++ b/console/src/main/java/org/apache/syncope/console/pages/DisplayAttributesModalPage.java
@@ -68,10 +68,8 @@ public class DisplayAttributesModalPage extends BaseModalPage {
 
     private final List<String> selectedDerSchemas;
 
-    private final List<String> selectedVirSchemas;
-
     public DisplayAttributesModalPage(final PageReference pageRef, final ModalWindow window,
-            final List<String> schemaNames, final List<String> dSchemaNames, final List<String> vSchemaNames) {
+            final List<String> schemaNames, final List<String> dSchemaNames) {
 
         super();
 
@@ -105,16 +103,6 @@ public class DisplayAttributesModalPage extends BaseModalPage {
             }
         };
 
-        final IModel<List<String>> vsnames = new LoadableDetachableModel<List<String>>() {
-
-            private static final long serialVersionUID = 5275935387613157437L;
-
-            @Override
-            protected List<String> load() {
-                return vSchemaNames;
-            }
-        };
-
         final Form form = new Form(FORM);
         form.setModel(new CompoundPropertyModel(this));
 
@@ -124,8 +112,6 @@ public class DisplayAttributesModalPage extends BaseModalPage {
 
         selectedDerSchemas = prefMan.getList(getRequest(), Constants.PREF_USERS_DERIVED_ATTRIBUTES_VIEW);
 
-        selectedVirSchemas = prefMan.getList(getRequest(), Constants.PREF_USERS_VIRTUAL_ATTRIBUTES_VIEW);
-
         final CheckGroup dgroup = new CheckGroup("dCheckGroup", new PropertyModel(this, "selectedDetails"));
         form.add(dgroup);
 
@@ -191,39 +177,13 @@ public class DisplayAttributesModalPage extends BaseModalPage {
             dsgroup.add(derSchemas);
         }
 
-        if (vsnames.getObject() == null || vsnames.getObject().isEmpty()) {
-            final Fragment fragment = new Fragment("vschemas", "emptyFragment", form);
-            form.add(fragment);
-
-            selectedVirSchemas.clear();
-        } else {
-            final Fragment fragment = new Fragment("vschemas", "vsfragment", form);
-            form.add(fragment);
-
-            final CheckGroup vsgroup = new CheckGroup("vsCheckGroup", new PropertyModel(this, "selectedVirSchemas"));
-            fragment.add(vsgroup);
-
-            final ListView<String> virSchemas = new ListView<String>("virSchemas", vsnames) {
-
-                private static final long serialVersionUID = 9101744072914090143L;
-
-                @Override
-                protected void populateItem(ListItem<String> item) {
-                    item.add(new Check("vscheck", item.getModel()));
-                    item.add(new Label("vsname", new ResourceModel(item.getModelObject(), item.getModelObject())));
-                }
-            };
-            vsgroup.add(virSchemas);
-        }
-
         final AjaxButton submit = new IndicatingAjaxButton(SUBMIT, new ResourceModel(SUBMIT)) {
 
             private static final long serialVersionUID = -4804368561204623354L;
 
             @Override
             protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
-                if (selectedDetails.size() + selectedSchemas.size() + selectedVirSchemas.size() + selectedDerSchemas.
-                        size()
+                if (selectedDetails.size() + selectedSchemas.size() + selectedDerSchemas.size()
                         > MAX_SELECTIONS) {
 
                     error(getString("tooManySelections"));
@@ -237,8 +197,6 @@ public class DisplayAttributesModalPage extends BaseModalPage {
 
                     prefs.put(Constants.PREF_USERS_DERIVED_ATTRIBUTES_VIEW, selectedDerSchemas);
 
-                    prefs.put(Constants.PREF_USERS_VIRTUAL_ATTRIBUTES_VIEW, selectedVirSchemas);
-
                     prefMan.setList(getRequest(), getResponse(), prefs);
 
                     ((BasePage) pageRef.getPage()).setModalResult(true);

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/console/src/main/java/org/apache/syncope/console/pages/panels/UserSearchResultPanel.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/pages/panels/UserSearchResultPanel.java b/console/src/main/java/org/apache/syncope/console/pages/panels/UserSearchResultPanel.java
index 84b9e31..c519c52 100644
--- a/console/src/main/java/org/apache/syncope/console/pages/panels/UserSearchResultPanel.java
+++ b/console/src/main/java/org/apache/syncope/console/pages/panels/UserSearchResultPanel.java
@@ -69,8 +69,6 @@ public class UserSearchResultPanel extends AbstractSearchResultPanel {
 
     private final List<String> dSchemaNames;
 
-    private final List<String> vSchemaNames;
-
     public <T extends AbstractAttributableTO> UserSearchResultPanel(final String id, final boolean filtered,
             final String fiql, final PageReference callerRef, final AbstractSubjectRestClient restClient) {
 
@@ -78,7 +76,6 @@ public class UserSearchResultPanel extends AbstractSearchResultPanel {
 
         this.schemaNames = schemaRestClient.getSchemaNames(AttributableType.USER);
         this.dSchemaNames = schemaRestClient.getDerSchemaNames(AttributableType.USER);
-        this.vSchemaNames = schemaRestClient.getVirSchemaNames(AttributableType.USER);
 
         initResultTable();
     }
@@ -113,12 +110,6 @@ public class UserSearchResultPanel extends AbstractSearchResultPanel {
             }
         }
 
-        for (String name : prefMan.getList(getRequest(), Constants.PREF_USERS_VIRTUAL_ATTRIBUTES_VIEW)) {
-            if (vSchemaNames.contains(name)) {
-                columns.add(new AttrColumn(name, SchemaType.VIRTUAL));
-            }
-        }
-
         // Add defaults in case of no selection
         if (columns.isEmpty()) {
             for (String name : DisplayAttributesModalPage.DEFAULT_SELECTION) {
@@ -252,8 +243,8 @@ public class UserSearchResultPanel extends AbstractSearchResultPanel {
 
                             @Override
                             public Page createPage() {
-                                return new DisplayAttributesModalPage(page.getPageReference(), displaymodal,
-                                        schemaNames, dSchemaNames, vSchemaNames);
+                                return new DisplayAttributesModalPage(
+                                        page.getPageReference(), displaymodal, schemaNames, dSchemaNames);
                             }
                         });
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/console/src/main/java/org/apache/syncope/console/rest/RoleRestClient.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/rest/RoleRestClient.java b/console/src/main/java/org/apache/syncope/console/rest/RoleRestClient.java
index e5a7587..a26a5cf 100644
--- a/console/src/main/java/org/apache/syncope/console/rest/RoleRestClient.java
+++ b/console/src/main/java/org/apache/syncope/console/rest/RoleRestClient.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.console.rest;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import javax.ws.rs.core.Response;
@@ -53,12 +54,17 @@ public class RoleRestClient extends AbstractSubjectRestClient {
     }
 
     public List<RoleTO> list() {
-        return getService(RoleService.class).list(1, 1000).getResult();
+        final List<RoleTO> allRoles = new ArrayList<RoleTO>();
+        final int count = count();
+        for (int page = 1; page <= (count / 100) + 1; page++) {
+            allRoles.addAll(getService(RoleService.class).list(page, 100, null, false).getResult());
+        }
+        return allRoles;
     }
 
     @Override
     public List<RoleTO> list(final int page, final int size, final SortParam<String> sort) {
-        return getService(RoleService.class).list(page, size, toOrderBy(sort)).getResult();
+        return getService(RoleService.class).list(page, size, toOrderBy(sort), false).getResult();
     }
 
     @Override
@@ -68,7 +74,7 @@ public class RoleRestClient extends AbstractSubjectRestClient {
 
     @Override
     public List<RoleTO> search(final String fiql, final int page, final int size, final SortParam<String> sort) {
-        return getService(RoleService.class).search(fiql, page, size, toOrderBy(sort)).getResult();
+        return getService(RoleService.class).search(fiql, page, size, toOrderBy(sort), false).getResult();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/console/src/main/java/org/apache/syncope/console/rest/UserRestClient.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/rest/UserRestClient.java b/console/src/main/java/org/apache/syncope/console/rest/UserRestClient.java
index f9e73ee..b064fd3 100644
--- a/console/src/main/java/org/apache/syncope/console/rest/UserRestClient.java
+++ b/console/src/main/java/org/apache/syncope/console/rest/UserRestClient.java
@@ -55,7 +55,7 @@ public class UserRestClient extends AbstractSubjectRestClient {
 
     @Override
     public List<UserTO> list(final int page, final int size, final SortParam<String> sort) {
-        return getService(UserService.class).list(page, size, toOrderBy(sort)).getResult();
+        return getService(UserService.class).list(page, size, toOrderBy(sort), false).getResult();
     }
 
     public UserTO create(final UserTO userTO, final boolean storePassword) {
@@ -101,7 +101,7 @@ public class UserRestClient extends AbstractSubjectRestClient {
 
     @Override
     public List<UserTO> search(final String fiql, final int page, final int size, final SortParam<String> sort) {
-        return getService(UserService.class).search(fiql, page, size, toOrderBy(sort)).getResult();
+        return getService(UserService.class).search(fiql, page, size, toOrderBy(sort), false).getResult();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/console/src/main/resources/org/apache/syncope/console/pages/DisplayAttributesModalPage.html
----------------------------------------------------------------------
diff --git a/console/src/main/resources/org/apache/syncope/console/pages/DisplayAttributesModalPage.html b/console/src/main/resources/org/apache/syncope/console/pages/DisplayAttributesModalPage.html
index 9d9d233..bb1ba9d 100644
--- a/console/src/main/resources/org/apache/syncope/console/pages/DisplayAttributesModalPage.html
+++ b/console/src/main/resources/org/apache/syncope/console/pages/DisplayAttributesModalPage.html
@@ -81,8 +81,6 @@ under the License.
 
         <span wicket:id="dschemas">[derived schemas]</span>
 
-        <span wicket:id="vschemas">[virtual schemas]</span>
-
       </div>
 
       <wicket:fragment wicket:id="sfragment">

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/console/src/test/java/org/apache/syncope/console/ReportTestITCase.java
----------------------------------------------------------------------
diff --git a/console/src/test/java/org/apache/syncope/console/ReportTestITCase.java b/console/src/test/java/org/apache/syncope/console/ReportTestITCase.java
index fae8233..59319a6 100644
--- a/console/src/test/java/org/apache/syncope/console/ReportTestITCase.java
+++ b/console/src/test/java/org/apache/syncope/console/ReportTestITCase.java
@@ -30,7 +30,7 @@ public class ReportTestITCase extends AbstractTest {
         seleniumDriver.findElement(By.xpath("//img[@alt=\"Reports\"]")).click();        
 
         wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//div[@id='tabs']")));
-        seleniumDriver.findElement(By.xpath("//table/tbody/tr/td[8]/div/span[13]/a")).click();
+        seleniumDriver.findElement(By.xpath("//table/tbody/tr/td[9]/div/span[13]/a")).click();
         
         wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//iframe")));
         seleniumDriver.switchTo().frame(0);
@@ -72,7 +72,7 @@ public class ReportTestITCase extends AbstractTest {
 
         wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//div[@id='tabs']")));
 
-        seleniumDriver.findElement(By.xpath("//table/tbody/tr/td[8]/div/span[6]/a")).click();
+        seleniumDriver.findElement(By.xpath("//table/tbody/tr/td[9]/div/span[6]/a")).click();
 
         wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("feedback")));
         assertTrue(seleniumDriver.findElement(By.tagName("body")).getText().contains("Operation executed successfully"));

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/notification/NotificationManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/notification/NotificationManager.java b/core/src/main/java/org/apache/syncope/core/notification/NotificationManager.java
index 48caa54..fead0a1 100644
--- a/core/src/main/java/org/apache/syncope/core/notification/NotificationManager.java
+++ b/core/src/main/java/org/apache/syncope/core/notification/NotificationManager.java
@@ -202,7 +202,7 @@ public class NotificationManager {
                 LOG.warn("{} cannot be notified: {} not found", recipient, notification.getRecipientAttrName());
             } else {
                 recipientEmails.add(email);
-                recipientTOs.add(userDataBinder.getUserTO(recipient));
+                recipientTOs.add(userDataBinder.getUserTO(recipient, true));
             }
         }
 
@@ -324,20 +324,20 @@ public class NotificationManager {
                     model.put("input", input);
 
                     if (subject instanceof SyncopeUser) {
-                        model.put("user", userDataBinder.getUserTO((SyncopeUser) subject));
+                        model.put("user", userDataBinder.getUserTO((SyncopeUser) subject, true));
                     } else if (subject instanceof SyncopeRole) {
-                        model.put("role", roleDataBinder.getRoleTO((SyncopeRole) subject));
+                        model.put("role", roleDataBinder.getRoleTO((SyncopeRole) subject, true));
                     }
                     NotificationTask notificationTask = getNotificationTask(notification, subject, model);
                     notificationTask = taskDAO.save(notificationTask);
-                    notificationList.add(notificationTask);                    
+                    notificationList.add(notificationTask);
                 }
             } else {
                 LOG.debug("Notification {}, userAbout {}, roleAbout {} is deactivated, "
                         + "notification task will not be created", notification.getId(),
                         notification.getUserAbout(), notification.getRoleAbout());
             }
-        }        
+        }
         return notificationList;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/report/RoleReportlet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/report/RoleReportlet.java b/core/src/main/java/org/apache/syncope/core/report/RoleReportlet.java
index dae1259..aa8b93f 100644
--- a/core/src/main/java/org/apache/syncope/core/report/RoleReportlet.java
+++ b/core/src/main/java/org/apache/syncope/core/report/RoleReportlet.java
@@ -231,7 +231,7 @@ public class RoleReportlet extends AbstractReportlet<RoleReportletConf> {
 
             // Using RoleTO for attribute values, since the conversion logic of
             // values to String is already encapsulated there
-            RoleTO roleTO = roleDataBinder.getRoleTO(role);
+            RoleTO roleTO = roleDataBinder.getRoleTO(role, true);
 
             doExtractAttributes(handler, roleTO, conf.getAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/report/UserReportlet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/report/UserReportlet.java b/core/src/main/java/org/apache/syncope/core/report/UserReportlet.java
index 30bdac0..a54ac53 100644
--- a/core/src/main/java/org/apache/syncope/core/report/UserReportlet.java
+++ b/core/src/main/java/org/apache/syncope/core/report/UserReportlet.java
@@ -269,7 +269,7 @@ public class UserReportlet extends AbstractReportlet<UserReportletConf> {
 
             // Using UserTO for attribute values, since the conversion logic of
             // values to String is already encapsulated there
-            UserTO userTO = userDataBinder.getUserTO(user);
+            UserTO userTO = userDataBinder.getUserTO(user, true);
 
             doExtractAttributes(handler, userTO, conf.getAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
 
@@ -293,7 +293,7 @@ public class UserReportlet extends AbstractReportlet<UserReportletConf> {
                             LOG.warn("Unexpected: cannot find membership for role {} for user {}", memb.getRoleId(),
                                     user);
                         } else {
-                            doExtractResources(handler, roleDataBinder.getRoleTO(actualMemb.getSyncopeRole()));
+                            doExtractResources(handler, roleDataBinder.getRoleTO(actualMemb.getSyncopeRole(), true));
                         }
                     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractSubjectController.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractSubjectController.java b/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractSubjectController.java
index 8ae6581..a7bb91f 100644
--- a/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractSubjectController.java
+++ b/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractSubjectController.java
@@ -35,9 +35,10 @@ public abstract class AbstractSubjectController<T extends AbstractSubjectTO, V e
 
     public abstract T delete(Long id);
 
-    public abstract List<T> list(int page, int size, List<OrderByClause> orderBy);
+    public abstract List<T> list(int page, int size, List<OrderByClause> orderBy, boolean includeDetails);
 
-    public abstract List<T> search(SearchCond searchCondition, int page, int size, List<OrderByClause> orderBy);
+    public abstract List<T> search(SearchCond searchCondition,
+            int page, int size, List<OrderByClause> orderBy, boolean includeDetails);
 
     public abstract int searchCount(SearchCond searchCondition);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java b/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java
index a64bdb5..c8fb25f 100644
--- a/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java
+++ b/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java
@@ -111,7 +111,7 @@ public class RoleController extends AbstractSubjectController<RoleTO, RoleMod> {
             throw new NotFoundException("Role " + roleId);
         }
 
-        return binder.getRoleTO(role);
+        return binder.getRoleTO(role, true);
     }
 
     @PreAuthorize("isAuthenticated() "
@@ -138,7 +138,7 @@ public class RoleController extends AbstractSubjectController<RoleTO, RoleMod> {
             throw new UnauthorizedRoleException(role.getId());
         }
 
-        return binder.getRoleTO(role);
+        return binder.getRoleTO(role, true);
     }
 
     @PreAuthorize("hasRole('ROLE_READ')")
@@ -153,12 +153,12 @@ public class RoleController extends AbstractSubjectController<RoleTO, RoleMod> {
 
         RoleTO result = role.getParent() == null
                 ? null
-                : binder.getRoleTO(role.getParent());
+                : binder.getRoleTO(role.getParent(), true);
 
         return result;
     }
 
-    @PreAuthorize("hasRole('ROLE_READ')")
+    @PreAuthorize("hasRole('ROLE_LIST')")
     @Transactional(readOnly = true)
     public List<RoleTO> children(final Long roleId) {
         SyncopeRole role = binder.getRoleFromId(roleId);
@@ -169,7 +169,7 @@ public class RoleController extends AbstractSubjectController<RoleTO, RoleMod> {
         List<RoleTO> childrenTOs = new ArrayList<RoleTO>(children.size());
         for (SyncopeRole child : children) {
             if (allowedRoleIds.contains(child.getId())) {
-                childrenTOs.add(binder.getRoleTO(child));
+                childrenTOs.add(binder.getRoleTO(child, true));
             }
         }
 
@@ -186,12 +186,14 @@ public class RoleController extends AbstractSubjectController<RoleTO, RoleMod> {
     @PreAuthorize("isAuthenticated()")
     @Transactional(readOnly = true)
     @Override
-    public List<RoleTO> list(final int page, final int size, final List<OrderByClause> orderBy) {
+    public List<RoleTO> list(
+            final int page, final int size, final List<OrderByClause> orderBy, final boolean details) {
+
         List<SyncopeRole> roles = roleDAO.findAll(page, size, orderBy);
 
         List<RoleTO> roleTOs = new ArrayList<RoleTO>(roles.size());
         for (SyncopeRole role : roles) {
-            roleTOs.add(binder.getRoleTO(role));
+            roleTOs.add(binder.getRoleTO(role, details));
         }
 
         return roleTOs;
@@ -209,7 +211,7 @@ public class RoleController extends AbstractSubjectController<RoleTO, RoleMod> {
     @Transactional(readOnly = true, rollbackFor = { Throwable.class })
     @Override
     public List<RoleTO> search(final SearchCond searchCondition, final int page, final int size,
-            final List<OrderByClause> orderBy) {
+            final List<OrderByClause> orderBy, final boolean details) {
 
         final List<SyncopeRole> matchingRoles = searchDAO.search(
                 EntitlementUtil.getRoleIds(EntitlementUtil.getOwnedEntitlementNames()),
@@ -217,7 +219,7 @@ public class RoleController extends AbstractSubjectController<RoleTO, RoleMod> {
 
         final List<RoleTO> result = new ArrayList<RoleTO>(matchingRoles.size());
         for (SyncopeRole role : matchingRoles) {
-            result.add(binder.getRoleTO(role));
+            result.add(binder.getRoleTO(role, details));
         }
 
         return result;
@@ -426,7 +428,7 @@ public class RoleController extends AbstractSubjectController<RoleTO, RoleMod> {
             propagationReporter.onPrimaryResourceFailure(tasks);
         }
 
-        final RoleTO updatedTO = binder.getRoleTO(role);
+        final RoleTO updatedTO = binder.getRoleTO(role, true);
         updatedTO.getPropagationStatusTOs().addAll(propagationReporter.getStatuses());
         return updatedTO;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java b/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
index dc5fdd2..5cbd5de 100644
--- a/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
+++ b/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
@@ -147,13 +147,15 @@ public class UserController extends AbstractSubjectController<UserTO, UserMod> {
     @PreAuthorize("hasRole('USER_LIST')")
     @Transactional(readOnly = true, rollbackFor = { Throwable.class })
     @Override
-    public List<UserTO> list(final int page, final int size, final List<OrderByClause> orderBy) {
+    public List<UserTO> list(
+            final int page, final int size, final List<OrderByClause> orderBy, final boolean details) {
+
         Set<Long> adminRoleIds = EntitlementUtil.getRoleIds(EntitlementUtil.getOwnedEntitlementNames());
 
         List<SyncopeUser> users = userDAO.findAll(adminRoleIds, page, size, orderBy);
         List<UserTO> userTOs = new ArrayList<UserTO>(users.size());
         for (SyncopeUser user : users) {
-            userTOs.add(binder.getUserTO(user));
+            userTOs.add(binder.getUserTO(user, details));
         }
 
         return userTOs;
@@ -177,7 +179,7 @@ public class UserController extends AbstractSubjectController<UserTO, UserMod> {
     @Transactional(readOnly = true)
     @Override
     public List<UserTO> search(final SearchCond searchCondition, final int page, final int size,
-            final List<OrderByClause> orderBy) {
+            final List<OrderByClause> orderBy, final boolean details) {
 
         final List<SyncopeUser> matchingUsers = searchDAO.search(
                 EntitlementUtil.getRoleIds(EntitlementUtil.getOwnedEntitlementNames()),
@@ -185,7 +187,7 @@ public class UserController extends AbstractSubjectController<UserTO, UserMod> {
 
         final List<UserTO> result = new ArrayList<UserTO>(matchingUsers.size());
         for (SyncopeUser user : matchingUsers) {
-            result.add(binder.getUserTO(user));
+            result.add(binder.getUserTO(user, details));
         }
 
         return result;
@@ -600,7 +602,7 @@ public class UserController extends AbstractSubjectController<UserTO, UserMod> {
             propagationReporter.onPrimaryResourceFailure(tasks);
         }
 
-        final UserTO updatedUserTO = binder.getUserTO(user);
+        final UserTO updatedUserTO = binder.getUserTO(user, true);
         updatedUserTO.getPropagationStatusTOs().addAll(propagationReporter.getStatuses());
         return updatedUserTO;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/rest/data/RoleDataBinder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/rest/data/RoleDataBinder.java b/core/src/main/java/org/apache/syncope/core/rest/data/RoleDataBinder.java
index 58029aa..fcd5c06 100644
--- a/core/src/main/java/org/apache/syncope/core/rest/data/RoleDataBinder.java
+++ b/core/src/main/java/org/apache/syncope/core/rest/data/RoleDataBinder.java
@@ -369,9 +369,7 @@ public class RoleDataBinder extends AbstractAttributableDataBinder {
 
     @SuppressWarnings("unchecked")
     @Transactional(readOnly = true)
-    public RoleTO getRoleTO(final SyncopeRole role) {
-        connObjectUtil.retrieveVirAttrValues(role, AttributableUtil.getInstance(AttributableType.ROLE));
-
+    public RoleTO getRoleTO(final SyncopeRole role, final boolean details) {
         RoleTO roleTO = new RoleTO();
 
         // set sys info
@@ -405,41 +403,6 @@ public class RoleDataBinder extends AbstractAttributableDataBinder {
             roleTO.setRoleOwner(role.getRoleOwner().getId());
         }
 
-        // -------------------------
-        // Retrieve all [derived/virtual] attributes (inherited and not)
-        // -------------------------        
-        final List<RAttr> allAttributes = role.findLastInheritedAncestorAttributes();
-
-        final List<RDerAttr> allDerAttributes = role.findLastInheritedAncestorDerivedAttributes();
-
-        final List<RVirAttr> allVirAttributes = role.findLastInheritedAncestorVirtualAttributes();
-        // -------------------------
-
-        fillTO(roleTO, allAttributes, allDerAttributes, allVirAttributes, role.getResources());
-
-        for (Entitlement entitlement : role.getEntitlements()) {
-            roleTO.getEntitlements().add(entitlement.getName());
-        }
-
-        for (RAttrTemplate template : role.findInheritedTemplates(RAttrTemplate.class)) {
-            roleTO.getRAttrTemplates().add(template.getSchema().getName());
-        }
-        for (RDerAttrTemplate template : role.findInheritedTemplates(RDerAttrTemplate.class)) {
-            roleTO.getRDerAttrTemplates().add(template.getSchema().getName());
-        }
-        for (RVirAttrTemplate template : role.findInheritedTemplates(RVirAttrTemplate.class)) {
-            roleTO.getRVirAttrTemplates().add(template.getSchema().getName());
-        }
-        for (MAttrTemplate template : role.findInheritedTemplates(MAttrTemplate.class)) {
-            roleTO.getMAttrTemplates().add(template.getSchema().getName());
-        }
-        for (MDerAttrTemplate template : role.findInheritedTemplates(MDerAttrTemplate.class)) {
-            roleTO.getMDerAttrTemplates().add(template.getSchema().getName());
-        }
-        for (MVirAttrTemplate template : role.findInheritedTemplates(MVirAttrTemplate.class)) {
-            roleTO.getMVirAttrTemplates().add(template.getSchema().getName());
-        }
-
         roleTO.setPasswordPolicy(role.getPasswordPolicy() == null
                 ? null
                 : role.getPasswordPolicy().getId());
@@ -447,11 +410,50 @@ public class RoleDataBinder extends AbstractAttributableDataBinder {
                 ? null
                 : role.getAccountPolicy().getId());
 
+        if (details) {
+            // -------------------------
+            // Retrieve all [derived/virtual] attributes (inherited and not)
+            // -------------------------        
+            connObjectUtil.retrieveVirAttrValues(role, AttributableUtil.getInstance(AttributableType.ROLE));
+
+            final List<RAttr> allAttributes = role.findLastInheritedAncestorAttributes();
+
+            final List<RDerAttr> allDerAttributes = role.findLastInheritedAncestorDerivedAttributes();
+
+            final List<RVirAttr> allVirAttributes = role.findLastInheritedAncestorVirtualAttributes();
+            // -------------------------
+
+            fillTO(roleTO, allAttributes, allDerAttributes, allVirAttributes, role.getResources());
+
+            for (Entitlement entitlement : role.getEntitlements()) {
+                roleTO.getEntitlements().add(entitlement.getName());
+            }
+
+            for (RAttrTemplate template : role.findInheritedTemplates(RAttrTemplate.class)) {
+                roleTO.getRAttrTemplates().add(template.getSchema().getName());
+            }
+            for (RDerAttrTemplate template : role.findInheritedTemplates(RDerAttrTemplate.class)) {
+                roleTO.getRDerAttrTemplates().add(template.getSchema().getName());
+            }
+            for (RVirAttrTemplate template : role.findInheritedTemplates(RVirAttrTemplate.class)) {
+                roleTO.getRVirAttrTemplates().add(template.getSchema().getName());
+            }
+            for (MAttrTemplate template : role.findInheritedTemplates(MAttrTemplate.class)) {
+                roleTO.getMAttrTemplates().add(template.getSchema().getName());
+            }
+            for (MDerAttrTemplate template : role.findInheritedTemplates(MDerAttrTemplate.class)) {
+                roleTO.getMDerAttrTemplates().add(template.getSchema().getName());
+            }
+            for (MVirAttrTemplate template : role.findInheritedTemplates(MVirAttrTemplate.class)) {
+                roleTO.getMVirAttrTemplates().add(template.getSchema().getName());
+            }
+        }
+
         return roleTO;
     }
 
     @Transactional(readOnly = true)
     public RoleTO getRoleTO(final Long roleId) {
-        return getRoleTO(getRoleFromId(roleId));
+        return getRoleTO(getRoleFromId(roleId), true);
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java b/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
index a904611..3ac56fc 100644
--- a/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
+++ b/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
@@ -163,7 +163,7 @@ public class UserDataBinder extends AbstractAttributableDataBinder {
             authUserTO.setUsername(adminUser);
         } else {
             SyncopeUser authUser = userDAO.find(authUsername);
-            authUserTO = getUserTO(authUser);
+            authUserTO = getUserTO(authUser, true);
         }
 
         return authUserTO;
@@ -327,7 +327,7 @@ public class UserDataBinder extends AbstractAttributableDataBinder {
                 // of the user object currently in memory (which has potentially
                 // some modifications compared to the one stored in the DB
                 membership = user.getMembership(membership.getSyncopeRole().getId());
-                if (membershipToBeAddedRoleIds.contains(membership.getSyncopeRole().getId())) {
+                if (membership != null && membershipToBeAddedRoleIds.contains(membership.getSyncopeRole().getId())) {
                     Set<Long> attributeIds = new HashSet<Long>(membership.getAttrs().size());
                     for (AbstractAttr attribute : membership.getAttrs()) {
                         attributeIds.add(attribute.getId());
@@ -413,7 +413,7 @@ public class UserDataBinder extends AbstractAttributableDataBinder {
     }
 
     @Transactional(readOnly = true)
-    public UserTO getUserTO(final SyncopeUser user) {
+    public UserTO getUserTO(final SyncopeUser user, final boolean details) {
         UserTO userTO = new UserTO();
 
         BeanUtils.copyProperties(user, userTO, IGNORE_USER_PROPERTIES);
@@ -422,31 +422,36 @@ public class UserDataBinder extends AbstractAttributableDataBinder {
             userTO.setSecurityQuestion(user.getSecurityQuestion().getId());
         }
 
-        connObjectUtil.retrieveVirAttrValues(user, AttributableUtil.getInstance(AttributableType.USER));
+        if (details) {
+            connObjectUtil.retrieveVirAttrValues(user, AttributableUtil.getInstance(AttributableType.USER));
+        }
+
         fillTO(userTO, user.getAttrs(), user.getDerAttrs(), user.getVirAttrs(), user.getResources());
 
-        MembershipTO membershipTO;
-        for (Membership membership : user.getMemberships()) {
-            membershipTO = new MembershipTO();
+        if (details) {
+            for (Membership membership : user.getMemberships()) {
+                MembershipTO membershipTO = new MembershipTO();
 
-            // set sys info
-            membershipTO.setCreator(membership.getCreator());
-            membershipTO.setCreationDate(membership.getCreationDate());
-            membershipTO.setLastModifier(membership.getLastModifier());
-            membershipTO.setLastChangeDate(membership.getLastChangeDate());
+                // set sys info
+                membershipTO.setCreator(membership.getCreator());
+                membershipTO.setCreationDate(membership.getCreationDate());
+                membershipTO.setLastModifier(membership.getLastModifier());
+                membershipTO.setLastChangeDate(membership.getLastChangeDate());
 
-            membershipTO.setId(membership.getId());
-            membershipTO.setRoleId(membership.getSyncopeRole().getId());
-            membershipTO.setRoleName(membership.getSyncopeRole().getName());
+                membershipTO.setId(membership.getId());
+                membershipTO.setRoleId(membership.getSyncopeRole().getId());
+                membershipTO.setRoleName(membership.getSyncopeRole().getName());
 
-            // SYNCOPE-458 retrieve also membership virtual attributes
-            connObjectUtil.retrieveVirAttrValues(membership, AttributableUtil.getInstance(AttributableType.MEMBERSHIP));
+                // SYNCOPE-458 retrieve also membership virtual attributes
+                connObjectUtil.retrieveVirAttrValues(membership, AttributableUtil.getInstance(
+                        AttributableType.MEMBERSHIP));
 
-            fillTO(membershipTO,
-                    membership.getAttrs(), membership.getDerAttrs(), membership.getVirAttrs(),
-                    Collections.<ExternalResource>emptyList());
+                fillTO(membershipTO,
+                        membership.getAttrs(), membership.getDerAttrs(), membership.getVirAttrs(),
+                        Collections.<ExternalResource>emptyList());
 
-            userTO.getMemberships().add(membershipTO);
+                userTO.getMemberships().add(membershipTO);
+            }
         }
 
         return userTO;
@@ -454,12 +459,12 @@ public class UserDataBinder extends AbstractAttributableDataBinder {
 
     @Transactional(readOnly = true)
     public UserTO getUserTO(final String username) {
-        return getUserTO(getUserFromUsername(username));
+        return getUserTO(getUserFromUsername(username), true);
     }
 
     @Transactional(readOnly = true)
     public UserTO getUserTO(final Long userId) {
-        return getUserTO(getUserFromId(userId));
+        return getUserTO(getUserFromId(userId), true);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java b/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java
index 91466a5..04d48ea 100644
--- a/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java
+++ b/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java
@@ -67,23 +67,25 @@ public class RoleServiceImpl extends AbstractServiceImpl implements RoleService
 
     @Override
     public PagedResult<RoleTO> list() {
-        return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, null);
+        return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, null, true);
     }
 
     @Override
     public PagedResult<RoleTO> list(final String orderBy) {
-        return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, orderBy);
+        return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, orderBy, true);
     }
 
     @Override
     public PagedResult<RoleTO> list(final Integer page, final Integer size) {
-        return list(page, size, null);
+        return list(page, size, null, true);
     }
 
     @Override
-    public PagedResult<RoleTO> list(final Integer page, final Integer size, final String orderBy) {
+    public PagedResult<RoleTO> list(
+            final Integer page, final Integer size, final String orderBy, final boolean details) {
+
         List<OrderByClause> orderByClauses = getOrderByClauses(orderBy);
-        return buildPagedResult(controller.list(page, size, orderByClauses), page, size, controller.count());
+        return buildPagedResult(controller.list(page, size, orderByClauses, details), page, size, controller.count());
     }
 
     @Override
@@ -98,25 +100,27 @@ public class RoleServiceImpl extends AbstractServiceImpl implements RoleService
 
     @Override
     public PagedResult<RoleTO> search(final String fiql) {
-        return search(fiql, DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, null);
+        return search(fiql, DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, null, true);
     }
 
     @Override
     public PagedResult<RoleTO> search(final String fiql, final String orderBy) {
-        return search(fiql, DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, orderBy);
+        return search(fiql, DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, orderBy, true);
     }
 
     @Override
     public PagedResult<RoleTO> search(final String fiql, final Integer page, final Integer size) {
-        return search(fiql, page, size, null);
+        return search(fiql, page, size, null, true);
     }
 
     @Override
-    public PagedResult<RoleTO> search(final String fiql, final Integer page, final Integer size, final String orderBy) {
+    public PagedResult<RoleTO> search(
+            final String fiql, final Integer page, final Integer size, final String orderBy, final boolean details) {
+
         SearchCond cond = getSearchCond(fiql);
         List<OrderByClause> orderByClauses = getOrderByClauses(orderBy);
         return buildPagedResult(
-                controller.search(cond, page, size, orderByClauses), page, size, controller.searchCount(cond));
+                controller.search(cond, page, size, orderByClauses, details), page, size, controller.searchCount(cond));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java b/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java
index e1287fe..7cd2ad7 100644
--- a/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java
+++ b/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java
@@ -79,23 +79,25 @@ public class UserServiceImpl extends AbstractServiceImpl implements UserService
 
     @Override
     public PagedResult<UserTO> list() {
-        return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, null);
+        return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, null, true);
     }
 
     @Override
     public PagedResult<UserTO> list(final String orderBy) {
-        return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, orderBy);
+        return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, orderBy, true);
     }
 
     @Override
     public PagedResult<UserTO> list(final Integer page, final Integer size) {
-        return list(page, size, null);
+        return list(page, size, null, true);
     }
 
     @Override
-    public PagedResult<UserTO> list(final Integer page, final Integer size, final String orderBy) {
+    public PagedResult<UserTO> list(
+            final Integer page, final Integer size, final String orderBy, final boolean details) {
+
         List<OrderByClause> orderByClauses = getOrderByClauses(orderBy);
-        return buildPagedResult(controller.list(page, size, orderByClauses), page, size, controller.count());
+        return buildPagedResult(controller.list(page, size, orderByClauses, details), page, size, controller.count());
     }
 
     @Override
@@ -105,25 +107,27 @@ public class UserServiceImpl extends AbstractServiceImpl implements UserService
 
     @Override
     public PagedResult<UserTO> search(final String fiql) {
-        return search(fiql, DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, null);
+        return search(fiql, DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, null, true);
     }
 
     @Override
     public PagedResult<UserTO> search(final String fiql, final String orderBy) {
-        return search(fiql, DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, orderBy);
+        return search(fiql, DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, orderBy, true);
     }
 
     @Override
     public PagedResult<UserTO> search(final String fiql, final Integer page, final Integer size) {
-        return search(fiql, page, size, null);
+        return search(fiql, page, size, null, true);
     }
 
     @Override
-    public PagedResult<UserTO> search(final String fiql, final Integer page, final Integer size, final String orderBy) {
+    public PagedResult<UserTO> search(
+            final String fiql, final Integer page, final Integer size, final String orderBy, final boolean details) {
+
         SearchCond cond = getSearchCond(fiql);
         List<OrderByClause> orderByClauses = getOrderByClauses(orderBy);
         return buildPagedResult(
-                controller.search(cond, page, size, orderByClauses), page, size, controller.searchCount(cond));
+                controller.search(cond, page, size, orderByClauses, details), page, size, controller.searchCount(cond));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java b/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java
index 18f7008..a6586b4 100644
--- a/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java
+++ b/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java
@@ -46,7 +46,7 @@ public class RolePushResultHandler extends AbstractSubjectPushResultHandler {
 
     @Override
     protected AbstractSubject deprovision(final AbstractSubject sbj) {
-        final RoleTO before = roleDataBinder.getRoleTO(SyncopeRole.class.cast(sbj));
+        final RoleTO before = roleDataBinder.getRoleTO(SyncopeRole.class.cast(sbj), true);
 
         final List<String> noPropResources = new ArrayList<String>(before.getResources());
         noPropResources.remove(profile.getSyncTask().getResource().getName());
@@ -58,7 +58,7 @@ public class RolePushResultHandler extends AbstractSubjectPushResultHandler {
 
     @Override
     protected AbstractSubject provision(final AbstractSubject sbj, final Boolean enabled) {
-        final RoleTO before = roleDataBinder.getRoleTO(SyncopeRole.class.cast(sbj));
+        final RoleTO before = roleDataBinder.getRoleTO(SyncopeRole.class.cast(sbj), true);
 
         final List<String> noPropResources = new ArrayList<String>(before.getResources());
         noPropResources.remove(profile.getSyncTask().getResource().getName());

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/ActivitiUserWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/ActivitiUserWorkflowAdapter.java b/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/ActivitiUserWorkflowAdapter.java
index e15942e..2873c67 100644
--- a/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/ActivitiUserWorkflowAdapter.java
+++ b/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/ActivitiUserWorkflowAdapter.java
@@ -393,7 +393,7 @@ public class ActivitiUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
     @Override
     protected void doRequestPasswordReset(final SyncopeUser user) throws WorkflowException {
         Map<String, Object> variables = new HashMap<String, Object>(2);
-        variables.put(USER_TO, userDataBinder.getUserTO(user));
+        variables.put(USER_TO, userDataBinder.getUserTO(user, true));
         variables.put(EVENT, "requestPasswordReset");
 
         doExecuteTask(user, "requestPasswordReset", variables);
@@ -407,7 +407,7 @@ public class ActivitiUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
         Map<String, Object> variables = new HashMap<String, Object>(4);
         variables.put(TOKEN, token);
         variables.put(PASSWORD, password);
-        variables.put(USER_TO, userDataBinder.getUserTO(user));
+        variables.put(USER_TO, userDataBinder.getUserTO(user, true));
         variables.put(EVENT, "confirmPasswordReset");
 
         doExecuteTask(user, "confirmPasswordReset", variables);

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/resources/indexes.xml
----------------------------------------------------------------------
diff --git a/core/src/main/resources/indexes.xml b/core/src/main/resources/indexes.xml
index c63d931..ff308d6 100644
--- a/core/src/main/resources/indexes.xml
+++ b/core/src/main/resources/indexes.xml
@@ -26,16 +26,42 @@ under the License.
   <entry key="UAttrValue_longvalueIndex">CREATE INDEX UAttrValue_longvalueIndex ON UAttrValue(longvalue)</entry>
   <entry key="UAttrValue_doublevalueIndex">CREATE INDEX UAttrValue_doublevalueIndex ON UAttrValue(doublevalue)</entry>
   <entry key="UAttrValue_booleanvalueIndex">CREATE INDEX UAttrValue_booleanvalueIndex ON UAttrValue(booleanvalue)</entry>
+
   <entry key="MAttrValue_stringvalueIndex">CREATE INDEX MAttrValue_stringvalueIndex ON MAttrValue(stringvalue)</entry>
   <entry key="MAttrValue_datevalueIndex">CREATE INDEX MAttrValue_datevalueIndex ON MAttrValue(datevalue)</entry>
   <entry key="MAttrValue_longvalueIndex">CREATE INDEX MAttrValue_longvalueIndex ON MAttrValue(longvalue)</entry>
   <entry key="MAttrValue_doublevalueIndex">CREATE INDEX MAttrValue_doublevalueIndex ON MAttrValue(doublevalue)</entry>
   <entry key="MAttrValue_booleanvalueIndex">CREATE INDEX MAttrValue_booleanvalueIndex ON MAttrValue(booleanvalue)</entry>
+
   <entry key="RAttrValue_stringvalueIndex">CREATE INDEX RAttrValue_stringvalueIndex ON RAttrValue(stringvalue)</entry>
   <entry key="RAttrValue_datevalueIndex">CREATE INDEX RAttrValue_datevalueIndex ON RAttrValue(datevalue)</entry>
   <entry key="RAttrValue_longvalueIndex">CREATE INDEX RAttrValue_longvalueIndex ON RAttrValue(longvalue)</entry>
   <entry key="RAttrValue_doublevalueIndex">CREATE INDEX RAttrValue_doublevalueIndex ON RAttrValue(doublevalue)</entry>
   <entry key="RAttrValue_booleanvalueIndex">CREATE INDEX RAttrValue_booleanvalueIndex ON RAttrValue(booleanvalue)</entry>
+
+  <entry key="Membership_syncopeRoleIndex">CREATE INDEX Membership_syncopeRoleIndex ON Membership(syncopeRole_id)</entry>
+  <entry key="Membership_syncopeUserIndex">CREATE INDEX Membership_syncopeUserIndex ON Membership(syncopeUser_id)</entry>
+
+  <entry key="RAttrValue_attributeIdIndex">CREATE INDEX RAttrValue_attribute_idIndex on RAttrValue(attribute_id)</entry>
+  <entry key="UAttrValue_attributeIdIndex">CREATE INDEX UAttrValue_attribute_idIndex on UAttrValue(attribute_id)</entry>
+  <entry key="MAttrValue_attributeIdIndex">CREATE INDEX MAttrValue_attribute_idIndex on MAttrValue(attribute_id)</entry>
+  <entry key="CAttrValue_attributeIdIndex">CREATE INDEX CAttrValue_attribute_idIndex on CAttrValue(attribute_id)</entry>
+
+  <entry key="RAttr_templateIdIndex">CREATE INDEX RAttr_template_idIndex on RAttr(template_id)</entry>
+  <entry key="MAttr_templateIdIndex">CREATE INDEX MAttr_template_idIndex on MAttr(template_id)</entry>
+  <entry key="RAttr_owner_id_index">CREATE INDEX RAttr_owner_id_index on RAttr(owner_id)</entry>
+  <entry key="UAttr_owner_id_index">CREATE INDEX UAttr_owner_id_index on UAttr(owner_id)</entry>
+  <entry key="MAttr_owner_id_index">CREATE INDEX MAttr_owner_id_index on MAttr(owner_id)</entry>
+
+  <entry key="RDerAttr_owner_id_index">CREATE INDEX RDerAttr_owner_id_index on RDerAttr(owner_id)</entry>
+  <entry key="UDerAttr_owner_id_index">CREATE INDEX UDerAttr_owner_id_index on UDerAttr(owner_id)</entry>
+  <entry key="MDerAttr_owner_id_index">CREATE INDEX MDerAttr_owner_id_index on MDerAttr(owner_id)</entry>
+
+  <entry key="RVirAttr_owner_id_index">CREATE INDEX RVirAttr_owner_id_index on RVirAttr(owner_id)</entry>
+  <entry key="UVirAttr_owner_id_index">CREATE INDEX UVirAttr_owner_id_index on UVirAttr(owner_id)</entry>
+  <entry key="MVirAttr_owner_id_index">CREATE INDEX MVirAttr_owner_id_index on MVirAttr(owner_id)</entry>
+
   <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON Task(executed)</entry>
+
   <entry key="ACT_RU_TASK_PARENT_TASK_ID_">CREATE INDEX ACT_RU_TASK_PARENT_TASK_ID_ ON ACT_RU_TASK(PARENT_TASK_ID_)</entry>
-</properties>
+</properties>
\ No newline at end of file


[2/7] syncope git commit: [SYNCOPE-676] Various improvements including a new optional 'details' boolean parameter for list and search, defaulting to true to keep backward compatibility

Posted by il...@apache.org.
[SYNCOPE-676] Various improvements including a new optional 'details' boolean parameter for list and search, defaulting to true to keep backward compatibility


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

Branch: refs/heads/master
Commit: 2612337228f1fbd3818f61c4dd3371b7bf1d37fb
Parents: e1c2587
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon Jul 6 15:58:37 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon Jul 6 15:58:37 2015 +0200

----------------------------------------------------------------------
 .../syncope/common/services/JAXRSService.java   |  2 +
 .../syncope/common/services/RoleService.java    |  8 +-
 .../syncope/common/services/UserService.java    |  8 +-
 .../syncope/console/commons/Constants.java      |  2 -
 .../pages/DisplayAttributesModalPage.java       | 46 +----------
 .../pages/panels/UserSearchResultPanel.java     | 13 +---
 .../syncope/console/rest/RoleRestClient.java    | 12 ++-
 .../syncope/console/rest/UserRestClient.java    |  4 +-
 .../pages/DisplayAttributesModalPage.html       |  2 -
 .../syncope/console/ReportTestITCase.java       |  4 +-
 .../core/notification/NotificationManager.java  | 10 +--
 .../syncope/core/report/RoleReportlet.java      |  2 +-
 .../syncope/core/report/UserReportlet.java      |  4 +-
 .../controller/AbstractSubjectController.java   |  5 +-
 .../core/rest/controller/RoleController.java    | 22 +++---
 .../core/rest/controller/UserController.java    | 12 +--
 .../syncope/core/rest/data/RoleDataBinder.java  | 80 ++++++++++----------
 .../syncope/core/rest/data/UserDataBinder.java  | 51 +++++++------
 .../syncope/core/services/RoleServiceImpl.java  | 24 +++---
 .../syncope/core/services/UserServiceImpl.java  | 24 +++---
 .../core/sync/impl/RolePushResultHandler.java   |  4 +-
 .../activiti/ActivitiUserWorkflowAdapter.java   |  4 +-
 core/src/main/resources/indexes.xml             | 28 ++++++-
 23 files changed, 189 insertions(+), 182 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/common/src/main/java/org/apache/syncope/common/services/JAXRSService.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/syncope/common/services/JAXRSService.java b/common/src/main/java/org/apache/syncope/common/services/JAXRSService.java
index c801bb1..757d298 100644
--- a/common/src/main/java/org/apache/syncope/common/services/JAXRSService.java
+++ b/common/src/main/java/org/apache/syncope/common/services/JAXRSService.java
@@ -36,4 +36,6 @@ public interface JAXRSService {
 
     final String PARAM_ORDERBY = "orderby";
 
+    final String PARAM_DETAILS = "details";
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/common/src/main/java/org/apache/syncope/common/services/RoleService.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/syncope/common/services/RoleService.java b/common/src/main/java/org/apache/syncope/common/services/RoleService.java
index 1f4e45b..9bd1588 100644
--- a/common/src/main/java/org/apache/syncope/common/services/RoleService.java
+++ b/common/src/main/java/org/apache/syncope/common/services/RoleService.java
@@ -138,6 +138,7 @@ public interface RoleService extends JAXRSService {
      * @param page result page number
      * @param size number of entries per page
      * @param orderBy list of ordering clauses, separated by comma
+     * @param details whether include all details or not, defaults to true
      * @return paged list of existing roles matching page/size conditions
      */
     @GET
@@ -145,7 +146,8 @@ public interface RoleService extends JAXRSService {
     PagedResult<RoleTO> list(
             @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
             @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size,
-            @QueryParam(PARAM_ORDERBY) String orderBy);
+            @QueryParam(PARAM_ORDERBY) String orderBy,
+            @QueryParam(PARAM_DETAILS) @DefaultValue("true") boolean details);
 
     /**
      * Returns a paged list of roles matching the provided FIQL search condition.
@@ -193,6 +195,7 @@ public interface RoleService extends JAXRSService {
      * @param page result page number
      * @param size number of entries per page
      * @param orderBy list of ordering clauses, separated by comma
+     * @param details whether include all details or not, defaults to true
      * @return paged list of roles matching the provided FIQL search condition
      */
     @GET
@@ -201,7 +204,8 @@ public interface RoleService extends JAXRSService {
     PagedResult<RoleTO> search(@QueryParam(PARAM_FIQL) String fiql,
             @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
             @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size,
-            @QueryParam(PARAM_ORDERBY) String orderBy);
+            @QueryParam(PARAM_ORDERBY) String orderBy,
+            @QueryParam(PARAM_DETAILS) @DefaultValue("true") boolean details);
 
     /**
      * Creates a new role.

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/common/src/main/java/org/apache/syncope/common/services/UserService.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/syncope/common/services/UserService.java b/common/src/main/java/org/apache/syncope/common/services/UserService.java
index c720218..270920f 100644
--- a/common/src/main/java/org/apache/syncope/common/services/UserService.java
+++ b/common/src/main/java/org/apache/syncope/common/services/UserService.java
@@ -131,6 +131,7 @@ public interface UserService extends JAXRSService {
      * @param page result page number
      * @param size number of entries per page
      * @param orderBy list of ordering clauses, separated by comma
+     * @param details whether include all details or not, defaults to true
      * @return paged list of existing users matching page/size conditions
      */
     @GET
@@ -138,7 +139,8 @@ public interface UserService extends JAXRSService {
     PagedResult<UserTO> list(
             @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
             @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size,
-            @QueryParam(PARAM_ORDERBY) String orderBy);
+            @QueryParam(PARAM_ORDERBY) String orderBy,
+            @QueryParam(PARAM_DETAILS) @DefaultValue("true") boolean details);
 
     /**
      * Returns a paged list of users matching the provided FIQL search condition.
@@ -185,6 +187,7 @@ public interface UserService extends JAXRSService {
      * @param page result page number
      * @param size number of entries per page
      * @param orderBy list of ordering clauses, separated by comma
+     * @param details whether include all details or not, defaults to true
      * @return paged list of users matching the provided FIQL search condition
      */
     @GET
@@ -193,7 +196,8 @@ public interface UserService extends JAXRSService {
     PagedResult<UserTO> search(@QueryParam(PARAM_FIQL) String fiql,
             @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
             @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size,
-            @QueryParam(PARAM_ORDERBY) String orderBy);
+            @QueryParam(PARAM_ORDERBY) String orderBy,
+            @QueryParam(PARAM_DETAILS) @DefaultValue("true") boolean details);
 
     /**
      * Creates a new user.

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/console/src/main/java/org/apache/syncope/console/commons/Constants.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/commons/Constants.java b/console/src/main/java/org/apache/syncope/console/commons/Constants.java
index 92f8d77..d43759d 100644
--- a/console/src/main/java/org/apache/syncope/console/commons/Constants.java
+++ b/console/src/main/java/org/apache/syncope/console/commons/Constants.java
@@ -46,8 +46,6 @@ public final class Constants {
 
     public static final String PREF_USERS_DERIVED_ATTRIBUTES_VIEW = "users.derived.attributes.view";
 
-    public static final String PREF_USERS_VIRTUAL_ATTRIBUTES_VIEW = "users.virtual.attributes.view";
-
     public static final String PREF_CONF_SCHEMA_PAGINATOR_ROWS = "conf.schema.paginator.rows";
 
     public static final String PREF_USER_SCHEMA_PAGINATOR_ROWS = "user.schema.paginator.rows";

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/console/src/main/java/org/apache/syncope/console/pages/DisplayAttributesModalPage.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/pages/DisplayAttributesModalPage.java b/console/src/main/java/org/apache/syncope/console/pages/DisplayAttributesModalPage.java
index 724b7cd..dd60881 100644
--- a/console/src/main/java/org/apache/syncope/console/pages/DisplayAttributesModalPage.java
+++ b/console/src/main/java/org/apache/syncope/console/pages/DisplayAttributesModalPage.java
@@ -68,10 +68,8 @@ public class DisplayAttributesModalPage extends BaseModalPage {
 
     private final List<String> selectedDerSchemas;
 
-    private final List<String> selectedVirSchemas;
-
     public DisplayAttributesModalPage(final PageReference pageRef, final ModalWindow window,
-            final List<String> schemaNames, final List<String> dSchemaNames, final List<String> vSchemaNames) {
+            final List<String> schemaNames, final List<String> dSchemaNames) {
 
         super();
 
@@ -105,16 +103,6 @@ public class DisplayAttributesModalPage extends BaseModalPage {
             }
         };
 
-        final IModel<List<String>> vsnames = new LoadableDetachableModel<List<String>>() {
-
-            private static final long serialVersionUID = 5275935387613157437L;
-
-            @Override
-            protected List<String> load() {
-                return vSchemaNames;
-            }
-        };
-
         final Form form = new Form(FORM);
         form.setModel(new CompoundPropertyModel(this));
 
@@ -124,8 +112,6 @@ public class DisplayAttributesModalPage extends BaseModalPage {
 
         selectedDerSchemas = prefMan.getList(getRequest(), Constants.PREF_USERS_DERIVED_ATTRIBUTES_VIEW);
 
-        selectedVirSchemas = prefMan.getList(getRequest(), Constants.PREF_USERS_VIRTUAL_ATTRIBUTES_VIEW);
-
         final CheckGroup dgroup = new CheckGroup("dCheckGroup", new PropertyModel(this, "selectedDetails"));
         form.add(dgroup);
 
@@ -191,39 +177,13 @@ public class DisplayAttributesModalPage extends BaseModalPage {
             dsgroup.add(derSchemas);
         }
 
-        if (vsnames.getObject() == null || vsnames.getObject().isEmpty()) {
-            final Fragment fragment = new Fragment("vschemas", "emptyFragment", form);
-            form.add(fragment);
-
-            selectedVirSchemas.clear();
-        } else {
-            final Fragment fragment = new Fragment("vschemas", "vsfragment", form);
-            form.add(fragment);
-
-            final CheckGroup vsgroup = new CheckGroup("vsCheckGroup", new PropertyModel(this, "selectedVirSchemas"));
-            fragment.add(vsgroup);
-
-            final ListView<String> virSchemas = new ListView<String>("virSchemas", vsnames) {
-
-                private static final long serialVersionUID = 9101744072914090143L;
-
-                @Override
-                protected void populateItem(ListItem<String> item) {
-                    item.add(new Check("vscheck", item.getModel()));
-                    item.add(new Label("vsname", new ResourceModel(item.getModelObject(), item.getModelObject())));
-                }
-            };
-            vsgroup.add(virSchemas);
-        }
-
         final AjaxButton submit = new IndicatingAjaxButton(SUBMIT, new ResourceModel(SUBMIT)) {
 
             private static final long serialVersionUID = -4804368561204623354L;
 
             @Override
             protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
-                if (selectedDetails.size() + selectedSchemas.size() + selectedVirSchemas.size() + selectedDerSchemas.
-                        size()
+                if (selectedDetails.size() + selectedSchemas.size() + selectedDerSchemas.size()
                         > MAX_SELECTIONS) {
 
                     error(getString("tooManySelections"));
@@ -237,8 +197,6 @@ public class DisplayAttributesModalPage extends BaseModalPage {
 
                     prefs.put(Constants.PREF_USERS_DERIVED_ATTRIBUTES_VIEW, selectedDerSchemas);
 
-                    prefs.put(Constants.PREF_USERS_VIRTUAL_ATTRIBUTES_VIEW, selectedVirSchemas);
-
                     prefMan.setList(getRequest(), getResponse(), prefs);
 
                     ((BasePage) pageRef.getPage()).setModalResult(true);

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/console/src/main/java/org/apache/syncope/console/pages/panels/UserSearchResultPanel.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/pages/panels/UserSearchResultPanel.java b/console/src/main/java/org/apache/syncope/console/pages/panels/UserSearchResultPanel.java
index 84b9e31..c519c52 100644
--- a/console/src/main/java/org/apache/syncope/console/pages/panels/UserSearchResultPanel.java
+++ b/console/src/main/java/org/apache/syncope/console/pages/panels/UserSearchResultPanel.java
@@ -69,8 +69,6 @@ public class UserSearchResultPanel extends AbstractSearchResultPanel {
 
     private final List<String> dSchemaNames;
 
-    private final List<String> vSchemaNames;
-
     public <T extends AbstractAttributableTO> UserSearchResultPanel(final String id, final boolean filtered,
             final String fiql, final PageReference callerRef, final AbstractSubjectRestClient restClient) {
 
@@ -78,7 +76,6 @@ public class UserSearchResultPanel extends AbstractSearchResultPanel {
 
         this.schemaNames = schemaRestClient.getSchemaNames(AttributableType.USER);
         this.dSchemaNames = schemaRestClient.getDerSchemaNames(AttributableType.USER);
-        this.vSchemaNames = schemaRestClient.getVirSchemaNames(AttributableType.USER);
 
         initResultTable();
     }
@@ -113,12 +110,6 @@ public class UserSearchResultPanel extends AbstractSearchResultPanel {
             }
         }
 
-        for (String name : prefMan.getList(getRequest(), Constants.PREF_USERS_VIRTUAL_ATTRIBUTES_VIEW)) {
-            if (vSchemaNames.contains(name)) {
-                columns.add(new AttrColumn(name, SchemaType.VIRTUAL));
-            }
-        }
-
         // Add defaults in case of no selection
         if (columns.isEmpty()) {
             for (String name : DisplayAttributesModalPage.DEFAULT_SELECTION) {
@@ -252,8 +243,8 @@ public class UserSearchResultPanel extends AbstractSearchResultPanel {
 
                             @Override
                             public Page createPage() {
-                                return new DisplayAttributesModalPage(page.getPageReference(), displaymodal,
-                                        schemaNames, dSchemaNames, vSchemaNames);
+                                return new DisplayAttributesModalPage(
+                                        page.getPageReference(), displaymodal, schemaNames, dSchemaNames);
                             }
                         });
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/console/src/main/java/org/apache/syncope/console/rest/RoleRestClient.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/rest/RoleRestClient.java b/console/src/main/java/org/apache/syncope/console/rest/RoleRestClient.java
index e5a7587..a26a5cf 100644
--- a/console/src/main/java/org/apache/syncope/console/rest/RoleRestClient.java
+++ b/console/src/main/java/org/apache/syncope/console/rest/RoleRestClient.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.console.rest;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import javax.ws.rs.core.Response;
@@ -53,12 +54,17 @@ public class RoleRestClient extends AbstractSubjectRestClient {
     }
 
     public List<RoleTO> list() {
-        return getService(RoleService.class).list(1, 1000).getResult();
+        final List<RoleTO> allRoles = new ArrayList<RoleTO>();
+        final int count = count();
+        for (int page = 1; page <= (count / 100) + 1; page++) {
+            allRoles.addAll(getService(RoleService.class).list(page, 100, null, false).getResult());
+        }
+        return allRoles;
     }
 
     @Override
     public List<RoleTO> list(final int page, final int size, final SortParam<String> sort) {
-        return getService(RoleService.class).list(page, size, toOrderBy(sort)).getResult();
+        return getService(RoleService.class).list(page, size, toOrderBy(sort), false).getResult();
     }
 
     @Override
@@ -68,7 +74,7 @@ public class RoleRestClient extends AbstractSubjectRestClient {
 
     @Override
     public List<RoleTO> search(final String fiql, final int page, final int size, final SortParam<String> sort) {
-        return getService(RoleService.class).search(fiql, page, size, toOrderBy(sort)).getResult();
+        return getService(RoleService.class).search(fiql, page, size, toOrderBy(sort), false).getResult();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/console/src/main/java/org/apache/syncope/console/rest/UserRestClient.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/rest/UserRestClient.java b/console/src/main/java/org/apache/syncope/console/rest/UserRestClient.java
index f9e73ee..b064fd3 100644
--- a/console/src/main/java/org/apache/syncope/console/rest/UserRestClient.java
+++ b/console/src/main/java/org/apache/syncope/console/rest/UserRestClient.java
@@ -55,7 +55,7 @@ public class UserRestClient extends AbstractSubjectRestClient {
 
     @Override
     public List<UserTO> list(final int page, final int size, final SortParam<String> sort) {
-        return getService(UserService.class).list(page, size, toOrderBy(sort)).getResult();
+        return getService(UserService.class).list(page, size, toOrderBy(sort), false).getResult();
     }
 
     public UserTO create(final UserTO userTO, final boolean storePassword) {
@@ -101,7 +101,7 @@ public class UserRestClient extends AbstractSubjectRestClient {
 
     @Override
     public List<UserTO> search(final String fiql, final int page, final int size, final SortParam<String> sort) {
-        return getService(UserService.class).search(fiql, page, size, toOrderBy(sort)).getResult();
+        return getService(UserService.class).search(fiql, page, size, toOrderBy(sort), false).getResult();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/console/src/main/resources/org/apache/syncope/console/pages/DisplayAttributesModalPage.html
----------------------------------------------------------------------
diff --git a/console/src/main/resources/org/apache/syncope/console/pages/DisplayAttributesModalPage.html b/console/src/main/resources/org/apache/syncope/console/pages/DisplayAttributesModalPage.html
index 9d9d233..bb1ba9d 100644
--- a/console/src/main/resources/org/apache/syncope/console/pages/DisplayAttributesModalPage.html
+++ b/console/src/main/resources/org/apache/syncope/console/pages/DisplayAttributesModalPage.html
@@ -81,8 +81,6 @@ under the License.
 
         <span wicket:id="dschemas">[derived schemas]</span>
 
-        <span wicket:id="vschemas">[virtual schemas]</span>
-
       </div>
 
       <wicket:fragment wicket:id="sfragment">

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/console/src/test/java/org/apache/syncope/console/ReportTestITCase.java
----------------------------------------------------------------------
diff --git a/console/src/test/java/org/apache/syncope/console/ReportTestITCase.java b/console/src/test/java/org/apache/syncope/console/ReportTestITCase.java
index fae8233..59319a6 100644
--- a/console/src/test/java/org/apache/syncope/console/ReportTestITCase.java
+++ b/console/src/test/java/org/apache/syncope/console/ReportTestITCase.java
@@ -30,7 +30,7 @@ public class ReportTestITCase extends AbstractTest {
         seleniumDriver.findElement(By.xpath("//img[@alt=\"Reports\"]")).click();        
 
         wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//div[@id='tabs']")));
-        seleniumDriver.findElement(By.xpath("//table/tbody/tr/td[8]/div/span[13]/a")).click();
+        seleniumDriver.findElement(By.xpath("//table/tbody/tr/td[9]/div/span[13]/a")).click();
         
         wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//iframe")));
         seleniumDriver.switchTo().frame(0);
@@ -72,7 +72,7 @@ public class ReportTestITCase extends AbstractTest {
 
         wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//div[@id='tabs']")));
 
-        seleniumDriver.findElement(By.xpath("//table/tbody/tr/td[8]/div/span[6]/a")).click();
+        seleniumDriver.findElement(By.xpath("//table/tbody/tr/td[9]/div/span[6]/a")).click();
 
         wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("feedback")));
         assertTrue(seleniumDriver.findElement(By.tagName("body")).getText().contains("Operation executed successfully"));

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/notification/NotificationManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/notification/NotificationManager.java b/core/src/main/java/org/apache/syncope/core/notification/NotificationManager.java
index 48caa54..fead0a1 100644
--- a/core/src/main/java/org/apache/syncope/core/notification/NotificationManager.java
+++ b/core/src/main/java/org/apache/syncope/core/notification/NotificationManager.java
@@ -202,7 +202,7 @@ public class NotificationManager {
                 LOG.warn("{} cannot be notified: {} not found", recipient, notification.getRecipientAttrName());
             } else {
                 recipientEmails.add(email);
-                recipientTOs.add(userDataBinder.getUserTO(recipient));
+                recipientTOs.add(userDataBinder.getUserTO(recipient, true));
             }
         }
 
@@ -324,20 +324,20 @@ public class NotificationManager {
                     model.put("input", input);
 
                     if (subject instanceof SyncopeUser) {
-                        model.put("user", userDataBinder.getUserTO((SyncopeUser) subject));
+                        model.put("user", userDataBinder.getUserTO((SyncopeUser) subject, true));
                     } else if (subject instanceof SyncopeRole) {
-                        model.put("role", roleDataBinder.getRoleTO((SyncopeRole) subject));
+                        model.put("role", roleDataBinder.getRoleTO((SyncopeRole) subject, true));
                     }
                     NotificationTask notificationTask = getNotificationTask(notification, subject, model);
                     notificationTask = taskDAO.save(notificationTask);
-                    notificationList.add(notificationTask);                    
+                    notificationList.add(notificationTask);
                 }
             } else {
                 LOG.debug("Notification {}, userAbout {}, roleAbout {} is deactivated, "
                         + "notification task will not be created", notification.getId(),
                         notification.getUserAbout(), notification.getRoleAbout());
             }
-        }        
+        }
         return notificationList;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/report/RoleReportlet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/report/RoleReportlet.java b/core/src/main/java/org/apache/syncope/core/report/RoleReportlet.java
index dae1259..aa8b93f 100644
--- a/core/src/main/java/org/apache/syncope/core/report/RoleReportlet.java
+++ b/core/src/main/java/org/apache/syncope/core/report/RoleReportlet.java
@@ -231,7 +231,7 @@ public class RoleReportlet extends AbstractReportlet<RoleReportletConf> {
 
             // Using RoleTO for attribute values, since the conversion logic of
             // values to String is already encapsulated there
-            RoleTO roleTO = roleDataBinder.getRoleTO(role);
+            RoleTO roleTO = roleDataBinder.getRoleTO(role, true);
 
             doExtractAttributes(handler, roleTO, conf.getAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/report/UserReportlet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/report/UserReportlet.java b/core/src/main/java/org/apache/syncope/core/report/UserReportlet.java
index 30bdac0..a54ac53 100644
--- a/core/src/main/java/org/apache/syncope/core/report/UserReportlet.java
+++ b/core/src/main/java/org/apache/syncope/core/report/UserReportlet.java
@@ -269,7 +269,7 @@ public class UserReportlet extends AbstractReportlet<UserReportletConf> {
 
             // Using UserTO for attribute values, since the conversion logic of
             // values to String is already encapsulated there
-            UserTO userTO = userDataBinder.getUserTO(user);
+            UserTO userTO = userDataBinder.getUserTO(user, true);
 
             doExtractAttributes(handler, userTO, conf.getAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
 
@@ -293,7 +293,7 @@ public class UserReportlet extends AbstractReportlet<UserReportletConf> {
                             LOG.warn("Unexpected: cannot find membership for role {} for user {}", memb.getRoleId(),
                                     user);
                         } else {
-                            doExtractResources(handler, roleDataBinder.getRoleTO(actualMemb.getSyncopeRole()));
+                            doExtractResources(handler, roleDataBinder.getRoleTO(actualMemb.getSyncopeRole(), true));
                         }
                     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractSubjectController.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractSubjectController.java b/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractSubjectController.java
index 8ae6581..a7bb91f 100644
--- a/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractSubjectController.java
+++ b/core/src/main/java/org/apache/syncope/core/rest/controller/AbstractSubjectController.java
@@ -35,9 +35,10 @@ public abstract class AbstractSubjectController<T extends AbstractSubjectTO, V e
 
     public abstract T delete(Long id);
 
-    public abstract List<T> list(int page, int size, List<OrderByClause> orderBy);
+    public abstract List<T> list(int page, int size, List<OrderByClause> orderBy, boolean includeDetails);
 
-    public abstract List<T> search(SearchCond searchCondition, int page, int size, List<OrderByClause> orderBy);
+    public abstract List<T> search(SearchCond searchCondition,
+            int page, int size, List<OrderByClause> orderBy, boolean includeDetails);
 
     public abstract int searchCount(SearchCond searchCondition);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java b/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java
index a64bdb5..c8fb25f 100644
--- a/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java
+++ b/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java
@@ -111,7 +111,7 @@ public class RoleController extends AbstractSubjectController<RoleTO, RoleMod> {
             throw new NotFoundException("Role " + roleId);
         }
 
-        return binder.getRoleTO(role);
+        return binder.getRoleTO(role, true);
     }
 
     @PreAuthorize("isAuthenticated() "
@@ -138,7 +138,7 @@ public class RoleController extends AbstractSubjectController<RoleTO, RoleMod> {
             throw new UnauthorizedRoleException(role.getId());
         }
 
-        return binder.getRoleTO(role);
+        return binder.getRoleTO(role, true);
     }
 
     @PreAuthorize("hasRole('ROLE_READ')")
@@ -153,12 +153,12 @@ public class RoleController extends AbstractSubjectController<RoleTO, RoleMod> {
 
         RoleTO result = role.getParent() == null
                 ? null
-                : binder.getRoleTO(role.getParent());
+                : binder.getRoleTO(role.getParent(), true);
 
         return result;
     }
 
-    @PreAuthorize("hasRole('ROLE_READ')")
+    @PreAuthorize("hasRole('ROLE_LIST')")
     @Transactional(readOnly = true)
     public List<RoleTO> children(final Long roleId) {
         SyncopeRole role = binder.getRoleFromId(roleId);
@@ -169,7 +169,7 @@ public class RoleController extends AbstractSubjectController<RoleTO, RoleMod> {
         List<RoleTO> childrenTOs = new ArrayList<RoleTO>(children.size());
         for (SyncopeRole child : children) {
             if (allowedRoleIds.contains(child.getId())) {
-                childrenTOs.add(binder.getRoleTO(child));
+                childrenTOs.add(binder.getRoleTO(child, true));
             }
         }
 
@@ -186,12 +186,14 @@ public class RoleController extends AbstractSubjectController<RoleTO, RoleMod> {
     @PreAuthorize("isAuthenticated()")
     @Transactional(readOnly = true)
     @Override
-    public List<RoleTO> list(final int page, final int size, final List<OrderByClause> orderBy) {
+    public List<RoleTO> list(
+            final int page, final int size, final List<OrderByClause> orderBy, final boolean details) {
+
         List<SyncopeRole> roles = roleDAO.findAll(page, size, orderBy);
 
         List<RoleTO> roleTOs = new ArrayList<RoleTO>(roles.size());
         for (SyncopeRole role : roles) {
-            roleTOs.add(binder.getRoleTO(role));
+            roleTOs.add(binder.getRoleTO(role, details));
         }
 
         return roleTOs;
@@ -209,7 +211,7 @@ public class RoleController extends AbstractSubjectController<RoleTO, RoleMod> {
     @Transactional(readOnly = true, rollbackFor = { Throwable.class })
     @Override
     public List<RoleTO> search(final SearchCond searchCondition, final int page, final int size,
-            final List<OrderByClause> orderBy) {
+            final List<OrderByClause> orderBy, final boolean details) {
 
         final List<SyncopeRole> matchingRoles = searchDAO.search(
                 EntitlementUtil.getRoleIds(EntitlementUtil.getOwnedEntitlementNames()),
@@ -217,7 +219,7 @@ public class RoleController extends AbstractSubjectController<RoleTO, RoleMod> {
 
         final List<RoleTO> result = new ArrayList<RoleTO>(matchingRoles.size());
         for (SyncopeRole role : matchingRoles) {
-            result.add(binder.getRoleTO(role));
+            result.add(binder.getRoleTO(role, details));
         }
 
         return result;
@@ -426,7 +428,7 @@ public class RoleController extends AbstractSubjectController<RoleTO, RoleMod> {
             propagationReporter.onPrimaryResourceFailure(tasks);
         }
 
-        final RoleTO updatedTO = binder.getRoleTO(role);
+        final RoleTO updatedTO = binder.getRoleTO(role, true);
         updatedTO.getPropagationStatusTOs().addAll(propagationReporter.getStatuses());
         return updatedTO;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java b/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
index dc5fdd2..5cbd5de 100644
--- a/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
+++ b/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
@@ -147,13 +147,15 @@ public class UserController extends AbstractSubjectController<UserTO, UserMod> {
     @PreAuthorize("hasRole('USER_LIST')")
     @Transactional(readOnly = true, rollbackFor = { Throwable.class })
     @Override
-    public List<UserTO> list(final int page, final int size, final List<OrderByClause> orderBy) {
+    public List<UserTO> list(
+            final int page, final int size, final List<OrderByClause> orderBy, final boolean details) {
+
         Set<Long> adminRoleIds = EntitlementUtil.getRoleIds(EntitlementUtil.getOwnedEntitlementNames());
 
         List<SyncopeUser> users = userDAO.findAll(adminRoleIds, page, size, orderBy);
         List<UserTO> userTOs = new ArrayList<UserTO>(users.size());
         for (SyncopeUser user : users) {
-            userTOs.add(binder.getUserTO(user));
+            userTOs.add(binder.getUserTO(user, details));
         }
 
         return userTOs;
@@ -177,7 +179,7 @@ public class UserController extends AbstractSubjectController<UserTO, UserMod> {
     @Transactional(readOnly = true)
     @Override
     public List<UserTO> search(final SearchCond searchCondition, final int page, final int size,
-            final List<OrderByClause> orderBy) {
+            final List<OrderByClause> orderBy, final boolean details) {
 
         final List<SyncopeUser> matchingUsers = searchDAO.search(
                 EntitlementUtil.getRoleIds(EntitlementUtil.getOwnedEntitlementNames()),
@@ -185,7 +187,7 @@ public class UserController extends AbstractSubjectController<UserTO, UserMod> {
 
         final List<UserTO> result = new ArrayList<UserTO>(matchingUsers.size());
         for (SyncopeUser user : matchingUsers) {
-            result.add(binder.getUserTO(user));
+            result.add(binder.getUserTO(user, details));
         }
 
         return result;
@@ -600,7 +602,7 @@ public class UserController extends AbstractSubjectController<UserTO, UserMod> {
             propagationReporter.onPrimaryResourceFailure(tasks);
         }
 
-        final UserTO updatedUserTO = binder.getUserTO(user);
+        final UserTO updatedUserTO = binder.getUserTO(user, true);
         updatedUserTO.getPropagationStatusTOs().addAll(propagationReporter.getStatuses());
         return updatedUserTO;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/rest/data/RoleDataBinder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/rest/data/RoleDataBinder.java b/core/src/main/java/org/apache/syncope/core/rest/data/RoleDataBinder.java
index 58029aa..fcd5c06 100644
--- a/core/src/main/java/org/apache/syncope/core/rest/data/RoleDataBinder.java
+++ b/core/src/main/java/org/apache/syncope/core/rest/data/RoleDataBinder.java
@@ -369,9 +369,7 @@ public class RoleDataBinder extends AbstractAttributableDataBinder {
 
     @SuppressWarnings("unchecked")
     @Transactional(readOnly = true)
-    public RoleTO getRoleTO(final SyncopeRole role) {
-        connObjectUtil.retrieveVirAttrValues(role, AttributableUtil.getInstance(AttributableType.ROLE));
-
+    public RoleTO getRoleTO(final SyncopeRole role, final boolean details) {
         RoleTO roleTO = new RoleTO();
 
         // set sys info
@@ -405,41 +403,6 @@ public class RoleDataBinder extends AbstractAttributableDataBinder {
             roleTO.setRoleOwner(role.getRoleOwner().getId());
         }
 
-        // -------------------------
-        // Retrieve all [derived/virtual] attributes (inherited and not)
-        // -------------------------        
-        final List<RAttr> allAttributes = role.findLastInheritedAncestorAttributes();
-
-        final List<RDerAttr> allDerAttributes = role.findLastInheritedAncestorDerivedAttributes();
-
-        final List<RVirAttr> allVirAttributes = role.findLastInheritedAncestorVirtualAttributes();
-        // -------------------------
-
-        fillTO(roleTO, allAttributes, allDerAttributes, allVirAttributes, role.getResources());
-
-        for (Entitlement entitlement : role.getEntitlements()) {
-            roleTO.getEntitlements().add(entitlement.getName());
-        }
-
-        for (RAttrTemplate template : role.findInheritedTemplates(RAttrTemplate.class)) {
-            roleTO.getRAttrTemplates().add(template.getSchema().getName());
-        }
-        for (RDerAttrTemplate template : role.findInheritedTemplates(RDerAttrTemplate.class)) {
-            roleTO.getRDerAttrTemplates().add(template.getSchema().getName());
-        }
-        for (RVirAttrTemplate template : role.findInheritedTemplates(RVirAttrTemplate.class)) {
-            roleTO.getRVirAttrTemplates().add(template.getSchema().getName());
-        }
-        for (MAttrTemplate template : role.findInheritedTemplates(MAttrTemplate.class)) {
-            roleTO.getMAttrTemplates().add(template.getSchema().getName());
-        }
-        for (MDerAttrTemplate template : role.findInheritedTemplates(MDerAttrTemplate.class)) {
-            roleTO.getMDerAttrTemplates().add(template.getSchema().getName());
-        }
-        for (MVirAttrTemplate template : role.findInheritedTemplates(MVirAttrTemplate.class)) {
-            roleTO.getMVirAttrTemplates().add(template.getSchema().getName());
-        }
-
         roleTO.setPasswordPolicy(role.getPasswordPolicy() == null
                 ? null
                 : role.getPasswordPolicy().getId());
@@ -447,11 +410,50 @@ public class RoleDataBinder extends AbstractAttributableDataBinder {
                 ? null
                 : role.getAccountPolicy().getId());
 
+        if (details) {
+            // -------------------------
+            // Retrieve all [derived/virtual] attributes (inherited and not)
+            // -------------------------        
+            connObjectUtil.retrieveVirAttrValues(role, AttributableUtil.getInstance(AttributableType.ROLE));
+
+            final List<RAttr> allAttributes = role.findLastInheritedAncestorAttributes();
+
+            final List<RDerAttr> allDerAttributes = role.findLastInheritedAncestorDerivedAttributes();
+
+            final List<RVirAttr> allVirAttributes = role.findLastInheritedAncestorVirtualAttributes();
+            // -------------------------
+
+            fillTO(roleTO, allAttributes, allDerAttributes, allVirAttributes, role.getResources());
+
+            for (Entitlement entitlement : role.getEntitlements()) {
+                roleTO.getEntitlements().add(entitlement.getName());
+            }
+
+            for (RAttrTemplate template : role.findInheritedTemplates(RAttrTemplate.class)) {
+                roleTO.getRAttrTemplates().add(template.getSchema().getName());
+            }
+            for (RDerAttrTemplate template : role.findInheritedTemplates(RDerAttrTemplate.class)) {
+                roleTO.getRDerAttrTemplates().add(template.getSchema().getName());
+            }
+            for (RVirAttrTemplate template : role.findInheritedTemplates(RVirAttrTemplate.class)) {
+                roleTO.getRVirAttrTemplates().add(template.getSchema().getName());
+            }
+            for (MAttrTemplate template : role.findInheritedTemplates(MAttrTemplate.class)) {
+                roleTO.getMAttrTemplates().add(template.getSchema().getName());
+            }
+            for (MDerAttrTemplate template : role.findInheritedTemplates(MDerAttrTemplate.class)) {
+                roleTO.getMDerAttrTemplates().add(template.getSchema().getName());
+            }
+            for (MVirAttrTemplate template : role.findInheritedTemplates(MVirAttrTemplate.class)) {
+                roleTO.getMVirAttrTemplates().add(template.getSchema().getName());
+            }
+        }
+
         return roleTO;
     }
 
     @Transactional(readOnly = true)
     public RoleTO getRoleTO(final Long roleId) {
-        return getRoleTO(getRoleFromId(roleId));
+        return getRoleTO(getRoleFromId(roleId), true);
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java b/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
index a904611..3ac56fc 100644
--- a/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
+++ b/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
@@ -163,7 +163,7 @@ public class UserDataBinder extends AbstractAttributableDataBinder {
             authUserTO.setUsername(adminUser);
         } else {
             SyncopeUser authUser = userDAO.find(authUsername);
-            authUserTO = getUserTO(authUser);
+            authUserTO = getUserTO(authUser, true);
         }
 
         return authUserTO;
@@ -327,7 +327,7 @@ public class UserDataBinder extends AbstractAttributableDataBinder {
                 // of the user object currently in memory (which has potentially
                 // some modifications compared to the one stored in the DB
                 membership = user.getMembership(membership.getSyncopeRole().getId());
-                if (membershipToBeAddedRoleIds.contains(membership.getSyncopeRole().getId())) {
+                if (membership != null && membershipToBeAddedRoleIds.contains(membership.getSyncopeRole().getId())) {
                     Set<Long> attributeIds = new HashSet<Long>(membership.getAttrs().size());
                     for (AbstractAttr attribute : membership.getAttrs()) {
                         attributeIds.add(attribute.getId());
@@ -413,7 +413,7 @@ public class UserDataBinder extends AbstractAttributableDataBinder {
     }
 
     @Transactional(readOnly = true)
-    public UserTO getUserTO(final SyncopeUser user) {
+    public UserTO getUserTO(final SyncopeUser user, final boolean details) {
         UserTO userTO = new UserTO();
 
         BeanUtils.copyProperties(user, userTO, IGNORE_USER_PROPERTIES);
@@ -422,31 +422,36 @@ public class UserDataBinder extends AbstractAttributableDataBinder {
             userTO.setSecurityQuestion(user.getSecurityQuestion().getId());
         }
 
-        connObjectUtil.retrieveVirAttrValues(user, AttributableUtil.getInstance(AttributableType.USER));
+        if (details) {
+            connObjectUtil.retrieveVirAttrValues(user, AttributableUtil.getInstance(AttributableType.USER));
+        }
+
         fillTO(userTO, user.getAttrs(), user.getDerAttrs(), user.getVirAttrs(), user.getResources());
 
-        MembershipTO membershipTO;
-        for (Membership membership : user.getMemberships()) {
-            membershipTO = new MembershipTO();
+        if (details) {
+            for (Membership membership : user.getMemberships()) {
+                MembershipTO membershipTO = new MembershipTO();
 
-            // set sys info
-            membershipTO.setCreator(membership.getCreator());
-            membershipTO.setCreationDate(membership.getCreationDate());
-            membershipTO.setLastModifier(membership.getLastModifier());
-            membershipTO.setLastChangeDate(membership.getLastChangeDate());
+                // set sys info
+                membershipTO.setCreator(membership.getCreator());
+                membershipTO.setCreationDate(membership.getCreationDate());
+                membershipTO.setLastModifier(membership.getLastModifier());
+                membershipTO.setLastChangeDate(membership.getLastChangeDate());
 
-            membershipTO.setId(membership.getId());
-            membershipTO.setRoleId(membership.getSyncopeRole().getId());
-            membershipTO.setRoleName(membership.getSyncopeRole().getName());
+                membershipTO.setId(membership.getId());
+                membershipTO.setRoleId(membership.getSyncopeRole().getId());
+                membershipTO.setRoleName(membership.getSyncopeRole().getName());
 
-            // SYNCOPE-458 retrieve also membership virtual attributes
-            connObjectUtil.retrieveVirAttrValues(membership, AttributableUtil.getInstance(AttributableType.MEMBERSHIP));
+                // SYNCOPE-458 retrieve also membership virtual attributes
+                connObjectUtil.retrieveVirAttrValues(membership, AttributableUtil.getInstance(
+                        AttributableType.MEMBERSHIP));
 
-            fillTO(membershipTO,
-                    membership.getAttrs(), membership.getDerAttrs(), membership.getVirAttrs(),
-                    Collections.<ExternalResource>emptyList());
+                fillTO(membershipTO,
+                        membership.getAttrs(), membership.getDerAttrs(), membership.getVirAttrs(),
+                        Collections.<ExternalResource>emptyList());
 
-            userTO.getMemberships().add(membershipTO);
+                userTO.getMemberships().add(membershipTO);
+            }
         }
 
         return userTO;
@@ -454,12 +459,12 @@ public class UserDataBinder extends AbstractAttributableDataBinder {
 
     @Transactional(readOnly = true)
     public UserTO getUserTO(final String username) {
-        return getUserTO(getUserFromUsername(username));
+        return getUserTO(getUserFromUsername(username), true);
     }
 
     @Transactional(readOnly = true)
     public UserTO getUserTO(final Long userId) {
-        return getUserTO(getUserFromId(userId));
+        return getUserTO(getUserFromId(userId), true);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java b/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java
index 91466a5..04d48ea 100644
--- a/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java
+++ b/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java
@@ -67,23 +67,25 @@ public class RoleServiceImpl extends AbstractServiceImpl implements RoleService
 
     @Override
     public PagedResult<RoleTO> list() {
-        return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, null);
+        return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, null, true);
     }
 
     @Override
     public PagedResult<RoleTO> list(final String orderBy) {
-        return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, orderBy);
+        return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, orderBy, true);
     }
 
     @Override
     public PagedResult<RoleTO> list(final Integer page, final Integer size) {
-        return list(page, size, null);
+        return list(page, size, null, true);
     }
 
     @Override
-    public PagedResult<RoleTO> list(final Integer page, final Integer size, final String orderBy) {
+    public PagedResult<RoleTO> list(
+            final Integer page, final Integer size, final String orderBy, final boolean details) {
+
         List<OrderByClause> orderByClauses = getOrderByClauses(orderBy);
-        return buildPagedResult(controller.list(page, size, orderByClauses), page, size, controller.count());
+        return buildPagedResult(controller.list(page, size, orderByClauses, details), page, size, controller.count());
     }
 
     @Override
@@ -98,25 +100,27 @@ public class RoleServiceImpl extends AbstractServiceImpl implements RoleService
 
     @Override
     public PagedResult<RoleTO> search(final String fiql) {
-        return search(fiql, DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, null);
+        return search(fiql, DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, null, true);
     }
 
     @Override
     public PagedResult<RoleTO> search(final String fiql, final String orderBy) {
-        return search(fiql, DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, orderBy);
+        return search(fiql, DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, orderBy, true);
     }
 
     @Override
     public PagedResult<RoleTO> search(final String fiql, final Integer page, final Integer size) {
-        return search(fiql, page, size, null);
+        return search(fiql, page, size, null, true);
     }
 
     @Override
-    public PagedResult<RoleTO> search(final String fiql, final Integer page, final Integer size, final String orderBy) {
+    public PagedResult<RoleTO> search(
+            final String fiql, final Integer page, final Integer size, final String orderBy, final boolean details) {
+
         SearchCond cond = getSearchCond(fiql);
         List<OrderByClause> orderByClauses = getOrderByClauses(orderBy);
         return buildPagedResult(
-                controller.search(cond, page, size, orderByClauses), page, size, controller.searchCount(cond));
+                controller.search(cond, page, size, orderByClauses, details), page, size, controller.searchCount(cond));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java b/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java
index e1287fe..7cd2ad7 100644
--- a/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java
+++ b/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java
@@ -79,23 +79,25 @@ public class UserServiceImpl extends AbstractServiceImpl implements UserService
 
     @Override
     public PagedResult<UserTO> list() {
-        return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, null);
+        return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, null, true);
     }
 
     @Override
     public PagedResult<UserTO> list(final String orderBy) {
-        return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, orderBy);
+        return list(DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, orderBy, true);
     }
 
     @Override
     public PagedResult<UserTO> list(final Integer page, final Integer size) {
-        return list(page, size, null);
+        return list(page, size, null, true);
     }
 
     @Override
-    public PagedResult<UserTO> list(final Integer page, final Integer size, final String orderBy) {
+    public PagedResult<UserTO> list(
+            final Integer page, final Integer size, final String orderBy, final boolean details) {
+
         List<OrderByClause> orderByClauses = getOrderByClauses(orderBy);
-        return buildPagedResult(controller.list(page, size, orderByClauses), page, size, controller.count());
+        return buildPagedResult(controller.list(page, size, orderByClauses, details), page, size, controller.count());
     }
 
     @Override
@@ -105,25 +107,27 @@ public class UserServiceImpl extends AbstractServiceImpl implements UserService
 
     @Override
     public PagedResult<UserTO> search(final String fiql) {
-        return search(fiql, DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, null);
+        return search(fiql, DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, null, true);
     }
 
     @Override
     public PagedResult<UserTO> search(final String fiql, final String orderBy) {
-        return search(fiql, DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, orderBy);
+        return search(fiql, DEFAULT_PARAM_PAGE_VALUE, DEFAULT_PARAM_SIZE_VALUE, orderBy, true);
     }
 
     @Override
     public PagedResult<UserTO> search(final String fiql, final Integer page, final Integer size) {
-        return search(fiql, page, size, null);
+        return search(fiql, page, size, null, true);
     }
 
     @Override
-    public PagedResult<UserTO> search(final String fiql, final Integer page, final Integer size, final String orderBy) {
+    public PagedResult<UserTO> search(
+            final String fiql, final Integer page, final Integer size, final String orderBy, final boolean details) {
+
         SearchCond cond = getSearchCond(fiql);
         List<OrderByClause> orderByClauses = getOrderByClauses(orderBy);
         return buildPagedResult(
-                controller.search(cond, page, size, orderByClauses), page, size, controller.searchCount(cond));
+                controller.search(cond, page, size, orderByClauses, details), page, size, controller.searchCount(cond));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java b/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java
index 18f7008..a6586b4 100644
--- a/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java
+++ b/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java
@@ -46,7 +46,7 @@ public class RolePushResultHandler extends AbstractSubjectPushResultHandler {
 
     @Override
     protected AbstractSubject deprovision(final AbstractSubject sbj) {
-        final RoleTO before = roleDataBinder.getRoleTO(SyncopeRole.class.cast(sbj));
+        final RoleTO before = roleDataBinder.getRoleTO(SyncopeRole.class.cast(sbj), true);
 
         final List<String> noPropResources = new ArrayList<String>(before.getResources());
         noPropResources.remove(profile.getSyncTask().getResource().getName());
@@ -58,7 +58,7 @@ public class RolePushResultHandler extends AbstractSubjectPushResultHandler {
 
     @Override
     protected AbstractSubject provision(final AbstractSubject sbj, final Boolean enabled) {
-        final RoleTO before = roleDataBinder.getRoleTO(SyncopeRole.class.cast(sbj));
+        final RoleTO before = roleDataBinder.getRoleTO(SyncopeRole.class.cast(sbj), true);
 
         final List<String> noPropResources = new ArrayList<String>(before.getResources());
         noPropResources.remove(profile.getSyncTask().getResource().getName());

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/ActivitiUserWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/ActivitiUserWorkflowAdapter.java b/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/ActivitiUserWorkflowAdapter.java
index e15942e..2873c67 100644
--- a/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/ActivitiUserWorkflowAdapter.java
+++ b/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/ActivitiUserWorkflowAdapter.java
@@ -393,7 +393,7 @@ public class ActivitiUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
     @Override
     protected void doRequestPasswordReset(final SyncopeUser user) throws WorkflowException {
         Map<String, Object> variables = new HashMap<String, Object>(2);
-        variables.put(USER_TO, userDataBinder.getUserTO(user));
+        variables.put(USER_TO, userDataBinder.getUserTO(user, true));
         variables.put(EVENT, "requestPasswordReset");
 
         doExecuteTask(user, "requestPasswordReset", variables);
@@ -407,7 +407,7 @@ public class ActivitiUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
         Map<String, Object> variables = new HashMap<String, Object>(4);
         variables.put(TOKEN, token);
         variables.put(PASSWORD, password);
-        variables.put(USER_TO, userDataBinder.getUserTO(user));
+        variables.put(USER_TO, userDataBinder.getUserTO(user, true));
         variables.put(EVENT, "confirmPasswordReset");
 
         doExecuteTask(user, "confirmPasswordReset", variables);

http://git-wip-us.apache.org/repos/asf/syncope/blob/26123372/core/src/main/resources/indexes.xml
----------------------------------------------------------------------
diff --git a/core/src/main/resources/indexes.xml b/core/src/main/resources/indexes.xml
index c63d931..ff308d6 100644
--- a/core/src/main/resources/indexes.xml
+++ b/core/src/main/resources/indexes.xml
@@ -26,16 +26,42 @@ under the License.
   <entry key="UAttrValue_longvalueIndex">CREATE INDEX UAttrValue_longvalueIndex ON UAttrValue(longvalue)</entry>
   <entry key="UAttrValue_doublevalueIndex">CREATE INDEX UAttrValue_doublevalueIndex ON UAttrValue(doublevalue)</entry>
   <entry key="UAttrValue_booleanvalueIndex">CREATE INDEX UAttrValue_booleanvalueIndex ON UAttrValue(booleanvalue)</entry>
+
   <entry key="MAttrValue_stringvalueIndex">CREATE INDEX MAttrValue_stringvalueIndex ON MAttrValue(stringvalue)</entry>
   <entry key="MAttrValue_datevalueIndex">CREATE INDEX MAttrValue_datevalueIndex ON MAttrValue(datevalue)</entry>
   <entry key="MAttrValue_longvalueIndex">CREATE INDEX MAttrValue_longvalueIndex ON MAttrValue(longvalue)</entry>
   <entry key="MAttrValue_doublevalueIndex">CREATE INDEX MAttrValue_doublevalueIndex ON MAttrValue(doublevalue)</entry>
   <entry key="MAttrValue_booleanvalueIndex">CREATE INDEX MAttrValue_booleanvalueIndex ON MAttrValue(booleanvalue)</entry>
+
   <entry key="RAttrValue_stringvalueIndex">CREATE INDEX RAttrValue_stringvalueIndex ON RAttrValue(stringvalue)</entry>
   <entry key="RAttrValue_datevalueIndex">CREATE INDEX RAttrValue_datevalueIndex ON RAttrValue(datevalue)</entry>
   <entry key="RAttrValue_longvalueIndex">CREATE INDEX RAttrValue_longvalueIndex ON RAttrValue(longvalue)</entry>
   <entry key="RAttrValue_doublevalueIndex">CREATE INDEX RAttrValue_doublevalueIndex ON RAttrValue(doublevalue)</entry>
   <entry key="RAttrValue_booleanvalueIndex">CREATE INDEX RAttrValue_booleanvalueIndex ON RAttrValue(booleanvalue)</entry>
+
+  <entry key="Membership_syncopeRoleIndex">CREATE INDEX Membership_syncopeRoleIndex ON Membership(syncopeRole_id)</entry>
+  <entry key="Membership_syncopeUserIndex">CREATE INDEX Membership_syncopeUserIndex ON Membership(syncopeUser_id)</entry>
+
+  <entry key="RAttrValue_attributeIdIndex">CREATE INDEX RAttrValue_attribute_idIndex on RAttrValue(attribute_id)</entry>
+  <entry key="UAttrValue_attributeIdIndex">CREATE INDEX UAttrValue_attribute_idIndex on UAttrValue(attribute_id)</entry>
+  <entry key="MAttrValue_attributeIdIndex">CREATE INDEX MAttrValue_attribute_idIndex on MAttrValue(attribute_id)</entry>
+  <entry key="CAttrValue_attributeIdIndex">CREATE INDEX CAttrValue_attribute_idIndex on CAttrValue(attribute_id)</entry>
+
+  <entry key="RAttr_templateIdIndex">CREATE INDEX RAttr_template_idIndex on RAttr(template_id)</entry>
+  <entry key="MAttr_templateIdIndex">CREATE INDEX MAttr_template_idIndex on MAttr(template_id)</entry>
+  <entry key="RAttr_owner_id_index">CREATE INDEX RAttr_owner_id_index on RAttr(owner_id)</entry>
+  <entry key="UAttr_owner_id_index">CREATE INDEX UAttr_owner_id_index on UAttr(owner_id)</entry>
+  <entry key="MAttr_owner_id_index">CREATE INDEX MAttr_owner_id_index on MAttr(owner_id)</entry>
+
+  <entry key="RDerAttr_owner_id_index">CREATE INDEX RDerAttr_owner_id_index on RDerAttr(owner_id)</entry>
+  <entry key="UDerAttr_owner_id_index">CREATE INDEX UDerAttr_owner_id_index on UDerAttr(owner_id)</entry>
+  <entry key="MDerAttr_owner_id_index">CREATE INDEX MDerAttr_owner_id_index on MDerAttr(owner_id)</entry>
+
+  <entry key="RVirAttr_owner_id_index">CREATE INDEX RVirAttr_owner_id_index on RVirAttr(owner_id)</entry>
+  <entry key="UVirAttr_owner_id_index">CREATE INDEX UVirAttr_owner_id_index on UVirAttr(owner_id)</entry>
+  <entry key="MVirAttr_owner_id_index">CREATE INDEX MVirAttr_owner_id_index on MVirAttr(owner_id)</entry>
+
   <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON Task(executed)</entry>
+
   <entry key="ACT_RU_TASK_PARENT_TASK_ID_">CREATE INDEX ACT_RU_TASK_PARENT_TASK_ID_ ON ACT_RU_TASK(PARENT_TASK_ID_)</entry>
-</properties>
+</properties>
\ No newline at end of file


[4/7] syncope git commit: [SYNCOPE-676] Merge from 1_2_X

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
index 6c5e0d6,0000000..b0fb8b0
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
@@@ -1,468 -1,0 +1,473 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.provisioning.java.data;
 +
 +import java.util.Collection;
 +import java.util.Date;
 +import java.util.HashSet;
 +import java.util.Map;
 +import java.util.Set;
 +import javax.annotation.Resource;
 +import org.apache.commons.collections4.Closure;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Transformer;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.syncope.common.lib.SyncopeClientCompositeException;
 +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.RelationshipTO;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.CipherAlgorithm;
 +import org.apache.syncope.common.lib.types.ClientExceptionType;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 +import org.apache.syncope.core.persistence.api.dao.SecurityQuestionDAO;
 +import org.apache.syncope.core.persistence.api.entity.group.Group;
 +import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 +import org.apache.syncope.core.misc.security.AuthContextUtils;
 +import org.apache.syncope.core.misc.security.Encryptor;
 +import org.apache.syncope.core.misc.spring.BeanUtils;
 +import org.apache.syncope.core.persistence.api.dao.RoleDAO;
 +import org.apache.syncope.core.persistence.api.entity.Role;
 +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 +import org.apache.syncope.core.persistence.api.entity.user.UMembership;
 +import org.apache.syncope.core.persistence.api.entity.user.URelationship;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.stereotype.Component;
 +import org.springframework.transaction.annotation.Transactional;
 +
 +@Component
 +@Transactional(rollbackFor = { Throwable.class })
 +public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDataBinder {
 +
 +    private static final String[] IGNORE_PROPERTIES = {
 +        "type", "realm", "auxClasses", "roles", "dynRoles", "relationships", "memberships", "dynGroups",
 +        "plainAttrs", "derAttrs", "virAttrs", "resources", "securityQuestion", "securityAnswer"
 +    };
 +
 +    @Autowired
 +    private RoleDAO roleDAO;
 +
 +    @Autowired
 +    private ConfDAO confDAO;
 +
 +    @Autowired
 +    private SecurityQuestionDAO securityQuestionDAO;
 +
 +    @Resource(name = "adminUser")
 +    private String adminUser;
 +
 +    @Resource(name = "anonymousUser")
 +    private String anonymousUser;
 +
 +    private final Encryptor encryptor = Encryptor.getInstance();
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public UserTO getAuthenticatedUserTO() {
 +        final UserTO authUserTO;
 +
 +        final String authUsername = AuthContextUtils.getAuthenticatedUsername();
 +        if (anonymousUser.equals(authUsername)) {
 +            authUserTO = new UserTO();
 +            authUserTO.setKey(-2);
 +            authUserTO.setUsername(anonymousUser);
 +        } else if (adminUser.equals(authUsername)) {
 +            authUserTO = new UserTO();
 +            authUserTO.setKey(-1);
 +            authUserTO.setUsername(adminUser);
 +        } else {
 +            User authUser = userDAO.find(authUsername);
-             authUserTO = getUserTO(authUser);
++            authUserTO = getUserTO(authUser, true);
 +        }
 +
 +        return authUserTO;
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public boolean verifyPassword(final String username, final String password) {
 +        return verifyPassword(userDAO.authFind(username), password);
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public boolean verifyPassword(final User user, final String password) {
 +        return encryptor.verify(password, user.getCipherAlgorithm(), user.getPassword());
 +    }
 +
 +    private void setPassword(final User user, final String password, final SyncopeClientCompositeException scce) {
 +        try {
 +            final String algorithm = confDAO.find(
 +                    "password.cipher.algorithm", CipherAlgorithm.AES.name()).getValues().get(0).getStringValue();
 +            CipherAlgorithm predefined = CipherAlgorithm.valueOf(algorithm);
 +            user.setPassword(password, predefined);
 +        } catch (IllegalArgumentException e) {
 +            final SyncopeClientException invalidCiperAlgorithm =
 +                    SyncopeClientException.build(ClientExceptionType.NotFound);
 +            invalidCiperAlgorithm.getElements().add(e.getMessage());
 +            scce.addException(invalidCiperAlgorithm);
 +
 +            throw scce;
 +        }
 +    }
 +
 +    @Override
 +    public void create(final User user, final UserTO userTO, final boolean storePassword) {
 +        SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
 +
 +        // roles
 +        for (Long roleKey : userTO.getRoles()) {
 +            Role role = roleDAO.find(roleKey);
 +            if (role == null) {
 +                LOG.warn("Ignoring unknown role with id {}", roleKey);
 +            } else {
 +                user.add(role);
 +            }
 +        }
 +
 +        // relationships
 +        for (RelationshipTO relationshipTO : userTO.getRelationships()) {
 +            AnyObject anyObject = anyObjectDAO.find(relationshipTO.getRightKey());
 +
 +            if (anyObject == null) {
 +                if (LOG.isDebugEnabled()) {
 +                    LOG.debug("Ignoring invalid anyObject " + relationshipTO.getRightKey());
 +                }
 +            } else {
 +                URelationship relationship = null;
 +                if (user.getKey() != null) {
 +                    relationship = user.getRelationship(anyObject.getKey());
 +                }
 +                if (relationship == null) {
 +                    relationship = entityFactory.newEntity(URelationship.class);
 +                    relationship.setRightEnd(anyObject);
 +                    relationship.setLeftEnd(user);
 +
 +                    user.add(relationship);
 +                }
 +            }
 +        }
 +
 +        // memberships
 +        for (MembershipTO membershipTO : userTO.getMemberships()) {
 +            Group group = groupDAO.find(membershipTO.getRightKey());
 +
 +            if (group == null) {
 +                if (LOG.isDebugEnabled()) {
 +                    LOG.debug("Ignoring invalid group " + membershipTO.getGroupName());
 +                }
 +            } else {
 +                UMembership membership = null;
 +                if (user.getKey() != null) {
 +                    membership = user.getMembership(group.getKey());
 +                }
 +                if (membership == null) {
 +                    membership = entityFactory.newEntity(UMembership.class);
 +                    membership.setRightEnd(group);
 +                    membership.setLeftEnd(user);
 +
 +                    user.add(membership);
 +                }
 +            }
 +        }
 +
 +        // realm, attributes, derived attributes, virtual attributes and resources
 +        fill(user, userTO, anyUtilsFactory.getInstance(AnyTypeKind.USER), scce);
 +
 +        // set password
 +        if (StringUtils.isBlank(userTO.getPassword()) || !storePassword) {
 +            LOG.debug("Password was not provided or not required to be stored");
 +        } else {
 +            setPassword(user, userTO.getPassword(), scce);
 +        }
 +
 +        // set username
 +        user.setUsername(userTO.getUsername());
 +
 +        // security question / answer
 +        if (userTO.getSecurityQuestion() != null) {
 +            SecurityQuestion securityQuestion = securityQuestionDAO.find(userTO.getSecurityQuestion());
 +            if (securityQuestion != null) {
 +                user.setSecurityQuestion(securityQuestion);
 +            }
 +        }
 +        user.setSecurityAnswer(userTO.getSecurityAnswer());
 +    }
 +
 +    @Override
 +    public PropagationByResource update(final User toBeUpdated, final UserMod userMod) {
 +        // Re-merge any pending change from workflow tasks
 +        final User user = userDAO.save(toBeUpdated);
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +
 +        SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
 +
 +        Collection<String> currentResources = userDAO.findAllResourceNames(user);
 +
 +        // fetch connObjectKeys before update
 +        Map<String, String> oldConnObjectKeys = getConnObjectKeys(user);
 +
 +        // realm
 +        setRealm(user, userMod);
 +
 +        // password
 +        if (StringUtils.isNotBlank(userMod.getPassword())) {
 +            setPassword(user, userMod.getPassword(), scce);
 +            user.setChangePwdDate(new Date());
 +            propByRes.addAll(ResourceOperation.UPDATE, currentResources);
 +        }
 +
 +        // username
 +        if (userMod.getUsername() != null && !userMod.getUsername().equals(user.getUsername())) {
 +            propByRes.addAll(ResourceOperation.UPDATE, currentResources);
 +
 +            user.setUsername(userMod.getUsername());
 +            AuthContextUtils.updateAuthenticatedUsername(userMod.getUsername());
 +        }
 +
 +        // security question / answer:
 +        // userMod.getSecurityQuestion() is null => remove user security question and answer
 +        // userMod.getSecurityQuestion() == 0 => don't change anything
 +        // userMod.getSecurityQuestion() > 0 => update user security question and answer
 +        if (userMod.getSecurityQuestion() == null) {
 +            user.setSecurityQuestion(null);
 +            user.setSecurityAnswer(null);
 +        } else if (userMod.getSecurityQuestion() > 0) {
 +            SecurityQuestion securityQuestion = securityQuestionDAO.find(userMod.getSecurityQuestion());
 +            if (securityQuestion != null) {
 +                user.setSecurityQuestion(securityQuestion);
 +                user.setSecurityAnswer(userMod.getSecurityAnswer());
 +            }
 +        }
 +
 +        // roles
 +        CollectionUtils.forAllDo(userMod.getRolesToRemove(), new Closure<Long>() {
 +
 +            @Override
 +            public void execute(final Long roleKey) {
 +                Role role = roleDAO.find(roleKey);
 +                if (role == null) {
 +                    LOG.warn("Ignoring unknown role with id {}", roleKey);
 +                } else {
 +                    user.remove(role);
 +                }
 +            }
 +        });
 +        CollectionUtils.forAllDo(userMod.getRolesToAdd(), new Closure<Long>() {
 +
 +            @Override
 +            public void execute(final Long roleKey) {
 +                Role role = roleDAO.find(roleKey);
 +                if (role == null) {
 +                    LOG.warn("Ignoring unknown role with id {}", roleKey);
 +                } else {
 +                    user.add(role);
 +                }
 +            }
 +        });
 +
 +        // attributes, derived attributes, virtual attributes and resources
 +        propByRes.merge(fill(user, userMod, anyUtilsFactory.getInstance(AnyTypeKind.USER), scce));
 +
 +        Set<String> toBeDeprovisioned = new HashSet<>();
 +        Set<String> toBeProvisioned = new HashSet<>();
 +
 +        // relationships to be removed
 +        for (Long anyObjectKey : userMod.getRelationshipsToRemove()) {
 +            LOG.debug("Relationship to be removed for any object {}", anyObjectKey);
 +
 +            URelationship relationship = user.getRelationship(anyObjectKey);
 +            if (relationship == null) {
 +                LOG.warn("Invalid anyObject key specified for relationship to be removed: {}", anyObjectKey);
 +            } else {
 +                if (!userMod.getRelationshipsToAdd().contains(anyObjectKey)) {
 +                    user.remove(relationship);
 +                    toBeDeprovisioned.addAll(relationship.getRightEnd().getResourceNames());
 +                }
 +            }
 +        }
 +
 +        // relationships to be added
 +        for (Long anyObjectKey : userMod.getRelationshipsToAdd()) {
 +            LOG.debug("Relationship to be added for any object {}", anyObjectKey);
 +
 +            AnyObject otherEnd = anyObjectDAO.find(anyObjectKey);
 +            if (otherEnd == null) {
 +                LOG.debug("Ignoring invalid any object {}", anyObjectKey);
 +            } else {
 +                URelationship relationship = user.getRelationship(otherEnd.getKey());
 +                if (relationship == null) {
 +                    relationship = entityFactory.newEntity(URelationship.class);
 +                    relationship.setRightEnd(otherEnd);
 +                    relationship.setLeftEnd(user);
 +
 +                    user.add(relationship);
 +
 +                    toBeProvisioned.addAll(otherEnd.getResourceNames());
 +                }
 +            }
 +        }
 +
 +        // memberships to be removed
 +        for (Long groupKey : userMod.getMembershipsToRemove()) {
 +            LOG.debug("Membership to be removed for group {}", groupKey);
 +
 +            UMembership membership = user.getMembership(groupKey);
 +            if (membership == null) {
 +                LOG.debug("Invalid group key specified for membership to be removed: {}", groupKey);
 +            } else {
 +                if (!userMod.getMembershipsToAdd().contains(groupKey)) {
 +                    user.remove(membership);
 +                    toBeDeprovisioned.addAll(membership.getRightEnd().getResourceNames());
 +                }
 +            }
 +        }
 +
 +        // memberships to be added
 +        for (Long groupKey : userMod.getMembershipsToAdd()) {
 +            LOG.debug("Membership to be added for group {}", groupKey);
 +
 +            Group group = groupDAO.find(groupKey);
 +            if (group == null) {
 +                LOG.debug("Ignoring invalid group {}", groupKey);
 +            } else {
 +                UMembership membership = user.getMembership(group.getKey());
 +                if (membership == null) {
 +                    membership = entityFactory.newEntity(UMembership.class);
 +                    membership.setRightEnd(group);
 +                    membership.setLeftEnd(user);
 +
 +                    user.add(membership);
 +
 +                    toBeProvisioned.addAll(group.getResourceNames());
 +                }
 +            }
 +        }
 +
 +        propByRes.addAll(ResourceOperation.DELETE, toBeDeprovisioned);
 +        propByRes.addAll(ResourceOperation.UPDATE, toBeProvisioned);
 +
 +        // In case of new memberships all current resources need to be updated in order to propagate new group
 +        // attribute values.
 +        if (!toBeDeprovisioned.isEmpty() || !toBeProvisioned.isEmpty()) {
 +            currentResources.removeAll(toBeDeprovisioned);
 +            propByRes.addAll(ResourceOperation.UPDATE, currentResources);
 +        }
 +
 +        // check if some connObjectKey was changed by the update above
 +        Map<String, String> newcCnnObjectKeys = getConnObjectKeys(user);
 +        for (Map.Entry<String, String> entry : oldConnObjectKeys.entrySet()) {
 +            if (newcCnnObjectKeys.containsKey(entry.getKey())
 +                    && !entry.getValue().equals(newcCnnObjectKeys.get(entry.getKey()))) {
 +
 +                propByRes.addOldConnObjectKey(entry.getKey(), entry.getValue());
 +                propByRes.add(ResourceOperation.UPDATE, entry.getKey());
 +            }
 +        }
 +
 +        return propByRes;
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
-     public UserTO getUserTO(final User user) {
++    public UserTO getUserTO(final User user, final boolean details) {
 +        UserTO userTO = new UserTO();
 +
 +        BeanUtils.copyProperties(user, userTO, IGNORE_PROPERTIES);
 +
 +        if (user.getSecurityQuestion() != null) {
 +            userTO.setSecurityQuestion(user.getSecurityQuestion().getKey());
 +        }
 +
-         virAttrHander.retrieveVirAttrValues(user);
++        if (details) {
++            virAttrHander.retrieveVirAttrValues(user);
++        }
++
 +        fillTO(userTO, user.getRealm().getFullPath(), user.getAuxClasses(),
 +                user.getPlainAttrs(), user.getDerAttrs(), user.getVirAttrs(), userDAO.findAllResources(user));
 +
-         // roles
-         CollectionUtils.collect(user.getRoles(), new Transformer<Role, Long>() {
++        if (details) {
++            // roles
++            CollectionUtils.collect(user.getRoles(), new Transformer<Role, Long>() {
 +
-             @Override
-             public Long transform(final Role role) {
-                 return role.getKey();
-             }
-         }, userTO.getRoles());
++                @Override
++                public Long transform(final Role role) {
++                    return role.getKey();
++                }
++            }, userTO.getRoles());
 +
-         // relationships
-         CollectionUtils.collect(user.getRelationships(), new Transformer<URelationship, RelationshipTO>() {
++            // relationships
++            CollectionUtils.collect(user.getRelationships(), new Transformer<URelationship, RelationshipTO>() {
 +
-             @Override
-             public RelationshipTO transform(final URelationship relationship) {
-                 return UserDataBinderImpl.this.getRelationshipTO(relationship);
-             }
++                @Override
++                public RelationshipTO transform(final URelationship relationship) {
++                    return UserDataBinderImpl.this.getRelationshipTO(relationship);
++                }
 +
-         }, userTO.getRelationships());
++            }, userTO.getRelationships());
 +
-         // memberships
-         CollectionUtils.collect(user.getMemberships(), new Transformer<UMembership, MembershipTO>() {
++            // memberships
++            CollectionUtils.collect(user.getMemberships(), new Transformer<UMembership, MembershipTO>() {
 +
-             @Override
-             public MembershipTO transform(final UMembership membership) {
-                 return UserDataBinderImpl.this.getMembershipTO(membership);
-             }
-         }, userTO.getMemberships());
++                @Override
++                public MembershipTO transform(final UMembership membership) {
++                    return UserDataBinderImpl.this.getMembershipTO(membership);
++                }
++            }, userTO.getMemberships());
 +
-         // dynamic memberships
-         CollectionUtils.collect(userDAO.findDynRoleMemberships(user), new Transformer<Role, Long>() {
++            // dynamic memberships
++            CollectionUtils.collect(userDAO.findDynRoleMemberships(user), new Transformer<Role, Long>() {
 +
-             @Override
-             public Long transform(final Role role) {
-                 return role.getKey();
-             }
-         }, userTO.getDynRoles());
-         CollectionUtils.collect(userDAO.findDynGroupMemberships(user), new Transformer<Group, Long>() {
++                @Override
++                public Long transform(final Role role) {
++                    return role.getKey();
++                }
++            }, userTO.getDynRoles());
++            CollectionUtils.collect(userDAO.findDynGroupMemberships(user), new Transformer<Group, Long>() {
 +
-             @Override
-             public Long transform(final Group group) {
-                 return group.getKey();
-             }
-         }, userTO.getDynGroups());
++                @Override
++                public Long transform(final Group group) {
++                    return group.getKey();
++                }
++            }, userTO.getDynGroups());
++        }
 +
 +        return userTO;
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public UserTO getUserTO(final String username) {
-         return getUserTO(userDAO.authFind(username));
++        return getUserTO(userDAO.authFind(username), true);
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public UserTO getUserTO(final Long key) {
-         return getUserTO(userDAO.authFind(key));
++        return getUserTO(userDAO.authFind(key), true);
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
index cdc540c,0000000..65b4618
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
@@@ -1,413 -1,0 +1,413 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.provisioning.java.notification;
 +
 +import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 +import java.io.StringWriter;
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.HashSet;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +import org.apache.syncope.common.lib.SyncopeConstants;
 +import org.apache.syncope.common.lib.to.GroupTO;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.AuditElements;
 +import org.apache.syncope.common.lib.types.AuditElements.Result;
 +import org.apache.syncope.common.lib.types.AuditLoggerName;
 +import org.apache.syncope.common.lib.types.IntMappingType;
 +import org.apache.syncope.common.lib.to.AnyObjectTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 +import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
 +import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 +import org.apache.syncope.core.persistence.api.dao.TaskDAO;
 +import org.apache.syncope.core.persistence.api.dao.UserDAO;
 +import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 +import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 +import org.apache.syncope.core.persistence.api.entity.Notification;
 +import org.apache.syncope.core.persistence.api.entity.PlainAttr;
 +import org.apache.syncope.core.persistence.api.entity.group.Group;
 +import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
 +import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 +import org.apache.syncope.core.persistence.api.entity.user.UDerAttr;
 +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
 +import org.apache.syncope.core.persistence.api.entity.user.UVirAttr;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
 +import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 +import org.apache.syncope.core.misc.search.SearchCondConverter;
 +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 +import org.apache.syncope.core.persistence.api.entity.Any;
 +import org.apache.syncope.core.persistence.api.entity.AnyAbout;
 +import org.apache.syncope.core.persistence.api.entity.AnyType;
 +import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 +import org.apache.velocity.VelocityContext;
 +import org.apache.velocity.app.VelocityEngine;
 +import org.apache.velocity.context.Context;
 +import org.apache.velocity.exception.VelocityException;
 +import org.apache.velocity.tools.ToolManager;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.stereotype.Component;
 +import org.springframework.transaction.annotation.Transactional;
 +
 +@Component
 +@Transactional(rollbackFor = { Throwable.class })
 +public class NotificationManagerImpl implements NotificationManager {
 +
 +    /**
 +     * Logger.
 +     */
 +    private static final Logger LOG = LoggerFactory.getLogger(NotificationManager.class);
 +
 +    public static final String MAIL_TEMPLATES = "mailTemplates/";
 +
 +    public static final String MAIL_TEMPLATE_HTML_SUFFIX = ".html.vm";
 +
 +    public static final String MAIL_TEMPLATE_TEXT_SUFFIX = ".txt.vm";
 +
 +    /**
 +     * Notification DAO.
 +     */
 +    @Autowired
 +    private NotificationDAO notificationDAO;
 +
 +    /**
 +     * Configuration DAO.
 +     */
 +    @Autowired
 +    private ConfDAO confDAO;
 +
 +    /**
 +     * AnyObject DAO.
 +     */
 +    @Autowired
 +    private AnyObjectDAO anyObjectDAO;
 +
 +    /**
 +     * User DAO.
 +     */
 +    @Autowired
 +    private UserDAO userDAO;
 +
 +    /**
 +     * Group DAO.
 +     */
 +    @Autowired
 +    private GroupDAO groupDAO;
 +
 +    /**
 +     * Search DAO.
 +     */
 +    @Autowired
 +    private AnySearchDAO searchDAO;
 +
 +    /**
 +     * Task DAO.
 +     */
 +    @Autowired
 +    private TaskDAO taskDAO;
 +
 +    /**
 +     * Velocity template engine.
 +     */
 +    @Autowired
 +    private VelocityEngine velocityEngine;
 +
 +    /**
 +     * Velocity tool manager.
 +     */
 +    @Autowired
 +    private ToolManager velocityToolManager;
 +
 +    @Autowired
 +    private VirAttrHandler virAttrHander;
 +
 +    @Autowired
 +    private UserDataBinder userDataBinder;
 +
 +    @Autowired
 +    private GroupDataBinder groupDataBinder;
 +
 +    @Autowired
 +    private EntityFactory entityFactory;
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public long getMaxRetries() {
 +        return confDAO.find("notification.maxRetries", "0").getValues().get(0).getLongValue();
 +    }
 +
 +    /**
 +     * Create a notification task.
 +     *
 +     * @param notification notification to take as model
 +     * @param any the any object this task is about
 +     * @param model Velocity model
 +     * @return notification task, fully populated
 +     */
 +    private NotificationTask getNotificationTask(
 +            final Notification notification,
 +            final Any<?, ?, ?> any,
 +            final Map<String, Object> model) {
 +
 +        if (any != null) {
 +            virAttrHander.retrieveVirAttrValues(any);
 +        }
 +
 +        List<User> recipients = new ArrayList<>();
 +
 +        if (notification.getRecipients() != null) {
 +            recipients.addAll(searchDAO.<User>search(SyncopeConstants.FULL_ADMIN_REALMS,
 +                    SearchCondConverter.convert(notification.getRecipients()),
 +                    Collections.<OrderByClause>emptyList(), AnyTypeKind.USER));
 +        }
 +
 +        if (notification.isSelfAsRecipient() && any instanceof User) {
 +            recipients.add((User) any);
 +        }
 +
 +        Set<String> recipientEmails = new HashSet<>();
 +        List<UserTO> recipientTOs = new ArrayList<>(recipients.size());
 +        for (User recipient : recipients) {
 +            virAttrHander.retrieveVirAttrValues(recipient);
 +
 +            String email = getRecipientEmail(notification.getRecipientAttrType(),
 +                    notification.getRecipientAttrName(), recipient);
 +            if (email == null) {
 +                LOG.warn("{} cannot be notified: {} not found", recipient, notification.getRecipientAttrName());
 +            } else {
 +                recipientEmails.add(email);
-                 recipientTOs.add(userDataBinder.getUserTO(recipient));
++                recipientTOs.add(userDataBinder.getUserTO(recipient, true));
 +            }
 +        }
 +
 +        if (notification.getStaticRecipients() != null) {
 +            recipientEmails.addAll(notification.getStaticRecipients());
 +        }
 +
 +        model.put("recipients", recipientTOs);
 +        model.put("syncopeConf", this.findAllSyncopeConfs());
 +        model.put("events", notification.getEvents());
 +
 +        NotificationTask task = entityFactory.newEntity(NotificationTask.class);
 +        task.setTraceLevel(notification.getTraceLevel());
 +        task.getRecipients().addAll(recipientEmails);
 +        task.setSender(notification.getSender());
 +        task.setSubject(notification.getSubject());
 +
 +        String htmlBody = mergeTemplateIntoString(
 +                MAIL_TEMPLATES + notification.getTemplate() + MAIL_TEMPLATE_HTML_SUFFIX, model);
 +        String textBody = mergeTemplateIntoString(
 +                MAIL_TEMPLATES + notification.getTemplate() + MAIL_TEMPLATE_TEXT_SUFFIX, model);
 +
 +        task.setHtmlBody(htmlBody);
 +        task.setTextBody(textBody);
 +
 +        return task;
 +    }
 +
 +    private String mergeTemplateIntoString(final String templateLocation, final Map<String, Object> model) {
 +        StringWriter result = new StringWriter();
 +        try {
 +            Context velocityContext = createVelocityContext(model);
 +            velocityEngine.mergeTemplate(templateLocation, SyncopeConstants.DEFAULT_ENCODING, velocityContext, result);
 +        } catch (VelocityException e) {
 +            LOG.error("Could not get mail body", e);
 +        } catch (RuntimeException e) {
 +            // ensure same behaviour as by using Spring VelocityEngineUtils.mergeTemplateIntoString()
 +            throw e;
 +        } catch (Exception e) {
 +            LOG.error("Could not get mail body", e);
 +        }
 +
 +        return result.toString();
 +    }
 +
 +    /**
 +     * Create a Velocity Context for the given model, to be passed to the template for merging.
 +     *
 +     * @param model Velocity model
 +     * @return Velocity context
 +     */
 +    protected Context createVelocityContext(final Map<String, Object> model) {
 +        Context toolContext = velocityToolManager.createContext();
 +        return new VelocityContext(model, toolContext);
 +    }
 +
 +    @Override
 +    public List<NotificationTask> createTasks(
 +            final AuditElements.EventCategoryType type,
 +            final String category,
 +            final String subcategory,
 +            final String event,
 +            final Result condition,
 +            final Object before,
 +            final Object output,
 +            final Object... input) {
 +
 +        Any<?, ?, ?> any = null;
 +
 +        if (before instanceof UserTO) {
 +            any = userDAO.find(((UserTO) before).getKey());
 +        } else if (output instanceof UserTO) {
 +            any = userDAO.find(((UserTO) output).getKey());
 +        } else if (before instanceof AnyObjectTO) {
 +            any = anyObjectDAO.find(((AnyObjectTO) before).getKey());
 +        } else if (output instanceof AnyObjectTO) {
 +            any = anyObjectDAO.find(((AnyObjectTO) output).getKey());
 +        } else if (before instanceof GroupTO) {
 +            any = groupDAO.find(((GroupTO) before).getKey());
 +        } else if (output instanceof GroupTO) {
 +            any = groupDAO.find(((GroupTO) output).getKey());
 +        }
 +
 +        AnyType anyType = any == null ? null : any.getType();
 +        LOG.debug("Search notification for [{}]{}", anyType, any);
 +
 +        List<NotificationTask> notifications = new ArrayList<>();
 +        for (Notification notification : notificationDAO.findAll()) {
 +            if (LOG.isDebugEnabled()) {
 +                for (AnyAbout about : notification.getAbouts()) {
 +                    LOG.debug("Notification about {} defined: {}", about.getAnyType(), about.get());
 +                }
 +            }
 +
 +            if (notification.isActive()) {
 +                String currentEvent = AuditLoggerName.buildEvent(type, category, subcategory, event, condition);
 +                if (!notification.getEvents().contains(currentEvent)) {
 +                    LOG.debug("No events found about {}", any);
 +                } else if (anyType == null || any == null
 +                        || notification.getAbout(anyType) == null
 +                        || searchDAO.matches(any,
 +                                SearchCondConverter.convert(notification.getAbout(anyType).get()), anyType.getKind())) {
 +
 +                    LOG.debug("Creating notification task for event {} about {}", currentEvent, any);
 +
 +                    final Map<String, Object> model = new HashMap<>();
 +                    model.put("type", type);
 +                    model.put("category", category);
 +                    model.put("subcategory", subcategory);
 +                    model.put("event", event);
 +                    model.put("condition", condition);
 +                    model.put("before", before);
 +                    model.put("output", output);
 +                    model.put("input", input);
 +
 +                    if (any instanceof User) {
-                         model.put("user", userDataBinder.getUserTO((User) any));
++                        model.put("user", userDataBinder.getUserTO((User) any, true));
 +                    } else if (any instanceof Group) {
-                         model.put("group", groupDataBinder.getGroupTO((Group) any));
++                        model.put("group", groupDataBinder.getGroupTO((Group) any, true));
 +                    }
 +
 +                    NotificationTask notificationTask = getNotificationTask(notification, any, model);
 +                    notificationTask = taskDAO.save(notificationTask);
 +                    notifications.add(notificationTask);
 +                }
 +            } else {
 +                LOG.debug("Notification {} is not active, task will not be created", notification.getKey());
 +            }
 +        }
 +        return notifications;
 +    }
 +
 +    private String getRecipientEmail(
 +            final IntMappingType recipientAttrType, final String recipientAttrName, final User user) {
 +
 +        String email = null;
 +
 +        switch (recipientAttrType) {
 +            case Username:
 +                email = user.getUsername();
 +                break;
 +
 +            case UserPlainSchema:
 +                UPlainAttr attr = user.getPlainAttr(recipientAttrName);
 +                if (attr != null) {
 +                    email = attr.getValuesAsStrings().isEmpty() ? null : attr.getValuesAsStrings().get(0);
 +                }
 +                break;
 +
 +            case UserDerivedSchema:
 +                UDerAttr derAttr = user.getDerAttr(recipientAttrName);
 +                if (derAttr != null) {
 +                    email = derAttr.getValue(user.getPlainAttrs());
 +                }
 +                break;
 +
 +            case UserVirtualSchema:
 +                UVirAttr virAttr = user.getVirAttr(recipientAttrName);
 +                if (virAttr != null) {
 +                    email = virAttr.getValues().isEmpty() ? null : virAttr.getValues().get(0);
 +                }
 +                break;
 +
 +            default:
 +        }
 +
 +        return email;
 +    }
 +
 +    @Override
 +    public TaskExec storeExec(final TaskExec execution) {
 +        NotificationTask task = taskDAO.find(execution.getTask().getKey());
 +        task.addExec(execution);
 +        task.setExecuted(true);
 +        taskDAO.save(task);
 +        // this flush call is needed to generate a value for the execution id
 +        taskDAO.flush();
 +        return execution;
 +    }
 +
 +    @Override
 +    public void setTaskExecuted(final Long taskId, final boolean executed) {
 +        NotificationTask task = taskDAO.find(taskId);
 +        task.setExecuted(executed);
 +        taskDAO.save(task);
 +    }
 +
 +    @Override
 +    public long countExecutionsWithStatus(final Long taskId, final String status) {
 +        NotificationTask task = taskDAO.find(taskId);
 +        long count = 0;
 +        for (TaskExec taskExec : task.getExecs()) {
 +            if (status == null) {
 +                if (taskExec.getStatus() == null) {
 +                    count++;
 +                }
 +            } else if (status.equals(taskExec.getStatus())) {
 +                count++;
 +            }
 +        }
 +        return count;
 +    }
 +
 +    protected Map<String, String> findAllSyncopeConfs() {
 +        Map<String, String> syncopeConfMap = new HashMap<>();
 +        for (PlainAttr<?> attr : confDAO.get().getPlainAttrs()) {
 +            syncopeConfMap.put(attr.getSchema().getKey(), attr.getValuesAsStrings().get(0));
 +        }
 +        return syncopeConfMap;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AnyObjectPushResultHandlerImpl.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AnyObjectPushResultHandlerImpl.java
index 503ce0b,0000000..1d00cc3
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AnyObjectPushResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AnyObjectPushResultHandlerImpl.java
@@@ -1,157 -1,0 +1,157 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.provisioning.java.sync;
 +
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.List;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.syncope.common.lib.mod.AnyObjectMod;
 +import org.apache.syncope.common.lib.to.AnyTO;
 +import org.apache.syncope.common.lib.to.AnyObjectTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.core.persistence.api.entity.Any;
 +import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 +import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 +import org.apache.syncope.core.provisioning.api.TimeoutException;
 +import org.apache.syncope.core.provisioning.api.sync.AnyObjectPushResultHandler;
 +import org.identityconnectors.framework.common.objects.ConnectorObject;
 +import org.identityconnectors.framework.common.objects.ObjectClass;
 +import org.identityconnectors.framework.common.objects.Uid;
 +
 +public class AnyObjectPushResultHandlerImpl extends AbstractPushResultHandler implements AnyObjectPushResultHandler {
 +
 +    @Override
 +    protected AnyUtils getAnyUtils() {
 +        return anyUtilsFactory.getInstance(AnyTypeKind.GROUP);
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> deprovision(final Any<?, ?, ?> sbj) {
-         AnyObjectTO before = anyObjectDataBinder.getAnyObjectTO(AnyObject.class.cast(sbj));
++        AnyObjectTO before = anyObjectDataBinder.getAnyObjectTO(AnyObject.class.cast(sbj), true);
 +
 +        List<String> noPropResources = new ArrayList<>(before.getResources());
 +        noPropResources.remove(profile.getTask().getResource().getKey());
 +
 +        taskExecutor.execute(propagationManager.getAnyObjectDeleteTasks(before.getKey(), noPropResources));
 +
 +        return anyObjectDAO.authFind(before.getKey());
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> provision(final Any<?, ?, ?> sbj, final Boolean enabled) {
-         AnyObjectTO before = anyObjectDataBinder.getAnyObjectTO(AnyObject.class.cast(sbj));
++        AnyObjectTO before = anyObjectDataBinder.getAnyObjectTO(AnyObject.class.cast(sbj), true);
 +
 +        List<String> noPropResources = new ArrayList<>(before.getResources());
 +        noPropResources.remove(profile.getTask().getResource().getKey());
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
 +
 +        taskExecutor.execute(propagationManager.getAnyObjectCreateTasks(
 +                before.getKey(),
 +                Collections.unmodifiableCollection(before.getVirAttrs()),
 +                propByRes,
 +                noPropResources));
 +
 +        return anyObjectDAO.authFind(before.getKey());
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> link(final Any<?, ?, ?> sbj, final Boolean unlink) {
 +        AnyObjectMod anyObjectMod = new AnyObjectMod();
 +        anyObjectMod.setKey(sbj.getKey());
 +
 +        if (unlink) {
 +            anyObjectMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
 +        } else {
 +            anyObjectMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
 +        }
 +
 +        awfAdapter.update(anyObjectMod);
 +
 +        return anyObjectDAO.authFind(sbj.getKey());
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> unassign(final Any<?, ?, ?> sbj) {
 +        AnyObjectMod anyObjectMod = new AnyObjectMod();
 +        anyObjectMod.setKey(sbj.getKey());
 +        anyObjectMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
 +        awfAdapter.update(anyObjectMod);
 +        return deprovision(sbj);
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> assign(final Any<?, ?, ?> sbj, final Boolean enabled) {
 +        AnyObjectMod anyObjectMod = new AnyObjectMod();
 +        anyObjectMod.setKey(sbj.getKey());
 +        anyObjectMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
 +        awfAdapter.update(anyObjectMod);
 +        return provision(sbj, enabled);
 +    }
 +
 +    @Override
 +    protected String getName(final Any<?, ?, ?> any) {
 +        return StringUtils.EMPTY;
 +    }
 +
 +    @Override
 +    protected AnyTO getAnyTO(final long key) {
 +        try {
 +            return anyObjectDataBinder.getAnyObjectTO(key);
 +        } catch (Exception e) {
 +            LOG.warn("Error retrieving user {}", key, e);
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> getAny(final long key) {
 +        try {
 +            return anyObjectDAO.authFind(key);
 +        } catch (Exception e) {
 +            LOG.warn("Error retrieving anyObject {}", key, e);
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    protected ConnectorObject getRemoteObject(final String connObjectKey, final ObjectClass objectClass) {
 +        ConnectorObject obj = null;
 +        try {
 +            Uid uid = new Uid(connObjectKey);
 +
 +            obj = profile.getConnector().getObject(
 +                    objectClass,
 +                    uid,
 +                    profile.getConnector().getOperationOptions(Collections.<MappingItem>emptySet()));
 +        } catch (TimeoutException toe) {
 +            LOG.debug("Request timeout", toe);
 +            throw toe;
 +        } catch (RuntimeException ignore) {
 +            LOG.debug("While resolving {}", connObjectKey, ignore);
 +        }
 +
 +        return obj;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/GroupPushResultHandlerImpl.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/GroupPushResultHandlerImpl.java
index 78fffbf,0000000..df26d58
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/GroupPushResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/GroupPushResultHandlerImpl.java
@@@ -1,156 -1,0 +1,156 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.provisioning.java.sync;
 +
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.List;
 +import org.apache.syncope.common.lib.mod.GroupMod;
 +import org.apache.syncope.common.lib.to.AnyTO;
 +import org.apache.syncope.common.lib.to.GroupTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.core.persistence.api.entity.Any;
 +import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 +import org.apache.syncope.core.persistence.api.entity.group.Group;
 +import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 +import org.apache.syncope.core.provisioning.api.TimeoutException;
 +import org.apache.syncope.core.provisioning.api.sync.GroupPushResultHandler;
 +import org.identityconnectors.framework.common.objects.ConnectorObject;
 +import org.identityconnectors.framework.common.objects.ObjectClass;
 +import org.identityconnectors.framework.common.objects.Uid;
 +
 +public class GroupPushResultHandlerImpl extends AbstractPushResultHandler implements GroupPushResultHandler {
 +
 +    @Override
 +    protected AnyUtils getAnyUtils() {
 +        return anyUtilsFactory.getInstance(AnyTypeKind.GROUP);
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> deprovision(final Any<?, ?, ?> sbj) {
-         GroupTO before = groupDataBinder.getGroupTO(Group.class.cast(sbj));
++        GroupTO before = groupDataBinder.getGroupTO(Group.class.cast(sbj), true);
 +
 +        List<String> noPropResources = new ArrayList<>(before.getResources());
 +        noPropResources.remove(profile.getTask().getResource().getKey());
 +
 +        taskExecutor.execute(propagationManager.getGroupDeleteTasks(before.getKey(), noPropResources));
 +
 +        return groupDAO.authFind(before.getKey());
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> provision(final Any<?, ?, ?> sbj, final Boolean enabled) {
-         GroupTO before = groupDataBinder.getGroupTO(Group.class.cast(sbj));
++        GroupTO before = groupDataBinder.getGroupTO(Group.class.cast(sbj), true);
 +
 +        List<String> noPropResources = new ArrayList<>(before.getResources());
 +        noPropResources.remove(profile.getTask().getResource().getKey());
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
 +
 +        taskExecutor.execute(propagationManager.getGroupCreateTasks(
 +                before.getKey(),
 +                Collections.unmodifiableCollection(before.getVirAttrs()),
 +                propByRes,
 +                noPropResources));
 +
 +        return groupDAO.authFind(before.getKey());
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> link(final Any<?, ?, ?> sbj, final Boolean unlink) {
 +        GroupMod groupMod = new GroupMod();
 +        groupMod.setKey(sbj.getKey());
 +
 +        if (unlink) {
 +            groupMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
 +        } else {
 +            groupMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
 +        }
 +
 +        gwfAdapter.update(groupMod);
 +
 +        return groupDAO.authFind(sbj.getKey());
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> unassign(final Any<?, ?, ?> sbj) {
 +        GroupMod groupMod = new GroupMod();
 +        groupMod.setKey(sbj.getKey());
 +        groupMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
 +        gwfAdapter.update(groupMod);
 +        return deprovision(sbj);
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> assign(final Any<?, ?, ?> sbj, final Boolean enabled) {
 +        GroupMod groupMod = new GroupMod();
 +        groupMod.setKey(sbj.getKey());
 +        groupMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
 +        gwfAdapter.update(groupMod);
 +        return provision(sbj, enabled);
 +    }
 +
 +    @Override
 +    protected String getName(final Any<?, ?, ?> any) {
 +        return Group.class.cast(any).getName();
 +    }
 +
 +    @Override
 +    protected AnyTO getAnyTO(final long key) {
 +        try {
 +            return groupDataBinder.getGroupTO(key);
 +        } catch (Exception e) {
 +            LOG.warn("Error retrieving user {}", key, e);
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> getAny(final long key) {
 +        try {
 +            return groupDAO.authFind(key);
 +        } catch (Exception e) {
 +            LOG.warn("Error retrieving group {}", key, e);
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    protected ConnectorObject getRemoteObject(final String connObjectKey, final ObjectClass objectClass) {
 +        ConnectorObject obj = null;
 +        try {
 +            Uid uid = new Uid(connObjectKey);
 +
 +            obj = profile.getConnector().getObject(
 +                    objectClass,
 +                    uid,
 +                    profile.getConnector().getOperationOptions(Collections.<MappingItem>emptySet()));
 +        } catch (TimeoutException toe) {
 +            LOG.debug("Request timeout", toe);
 +            throw toe;
 +        } catch (RuntimeException ignore) {
 +            LOG.debug("While resolving {}", connObjectKey, ignore);
 +        }
 +
 +        return obj;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
----------------------------------------------------------------------
diff --cc core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
index 93afe01,0000000..600b6d0
mode 100644,000000..100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
@@@ -1,289 -1,0 +1,291 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.rest.cxf.service;
 +
 +import static org.apache.syncope.core.rest.cxf.service.AbstractServiceImpl.LOG;
 +
 +import java.util.List;
 +import javax.ws.rs.core.Response;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Transformer;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.syncope.common.lib.SyncopeConstants;
 +import org.apache.syncope.common.lib.mod.AnyMod;
 +import org.apache.syncope.common.lib.mod.ResourceAssociationMod;
 +import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.common.lib.to.AnyTO;
 +import org.apache.syncope.common.lib.to.BulkAction;
 +import org.apache.syncope.common.lib.to.BulkActionResult;
 +import org.apache.syncope.common.lib.to.PagedResult;
 +import org.apache.syncope.common.lib.to.PropagationStatus;
 +import org.apache.syncope.common.lib.types.ResourceAssociationActionType;
 +import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
 +import org.apache.syncope.common.lib.wrap.ResourceKey;
 +import org.apache.syncope.common.rest.api.CollectionWrapper;
 +import org.apache.syncope.common.rest.api.beans.AnyListQuery;
 +import org.apache.syncope.common.rest.api.beans.AnySearchQuery;
 +import org.apache.syncope.common.rest.api.service.AnyService;
 +import org.apache.syncope.core.logic.AbstractAnyLogic;
 +import org.apache.syncope.core.logic.UserLogic;
 +import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 +
 +public abstract class AbstractAnyService<TO extends AnyTO, MOD extends AnyMod>
 +        extends AbstractServiceImpl
 +        implements AnyService<TO, MOD> {
 +
 +    protected abstract AbstractAnyLogic<TO, MOD> getAnyLogic();
 +
 +    @Override
 +    public TO read(final Long key) {
 +        return getAnyLogic().read(key);
 +    }
 +
 +    @Override
 +    public PagedResult<TO> list(final AnyListQuery listQuery) {
 +        CollectionUtils.transform(listQuery.getRealms(), new Transformer<String, String>() {
 +
 +            @Override
 +            public String transform(final String input) {
 +                return StringUtils.prependIfMissing(input, SyncopeConstants.ROOT_REALM);
 +            }
 +        });
 +
 +        return buildPagedResult(
 +                getAnyLogic().list(
 +                        listQuery.getPage(),
 +                        listQuery.getSize(),
 +                        getOrderByClauses(listQuery.getOrderBy()),
-                         listQuery.getRealms()),
++                        listQuery.getRealms(),
++                        listQuery.isDetails()),
 +                listQuery.getPage(),
 +                listQuery.getSize(),
 +                getAnyLogic().count(listQuery.getRealms()));
 +    }
 +
 +    @Override
 +    public PagedResult<TO> search(final AnySearchQuery searchQuery) {
 +        CollectionUtils.transform(searchQuery.getRealms(), new Transformer<String, String>() {
 +
 +            @Override
 +            public String transform(final String input) {
 +                return StringUtils.prependIfMissing(input, SyncopeConstants.ROOT_REALM);
 +            }
 +        });
 +
 +        SearchCond cond = getSearchCond(searchQuery.getFiql());
 +        return buildPagedResult(
 +                getAnyLogic().search(
 +                        cond,
 +                        searchQuery.getPage(),
 +                        searchQuery.getSize(),
 +                        getOrderByClauses(searchQuery.getOrderBy()),
-                         searchQuery.getRealms()),
++                        searchQuery.getRealms(),
++                        searchQuery.isDetails()),
 +                searchQuery.getPage(),
 +                searchQuery.getSize(),
 +                getAnyLogic().searchCount(cond, searchQuery.getRealms()));
 +    }
 +
 +    @Override
 +    public Response create(final TO anyTO) {
 +        TO created = getAnyLogic().create(anyTO);
 +        return createResponse(created.getKey(), created);
 +    }
 +
 +    @Override
 +    public Response update(final MOD anyMod) {
 +        TO any = getAnyLogic().read(anyMod.getKey());
 +
 +        checkETag(any.getETagValue());
 +
 +        TO updated = getAnyLogic().update(anyMod);
 +        return modificationResponse(updated);
 +    }
 +
 +    @Override
 +    public Response delete(final Long key) {
 +        TO group = getAnyLogic().read(key);
 +
 +        checkETag(group.getETagValue());
 +
 +        TO deleted = getAnyLogic().delete(key);
 +        return modificationResponse(deleted);
 +    }
 +
 +    @Override
 +    public Response bulkDeassociation(
 +            final Long key, final ResourceDeassociationActionType type, final List<ResourceKey> resourceNames) {
 +
 +        TO any = getAnyLogic().read(key);
 +
 +        checkETag(any.getETagValue());
 +
 +        TO updated;
 +        switch (type) {
 +            case UNLINK:
 +                updated = getAnyLogic().unlink(key, CollectionWrapper.unwrap(resourceNames));
 +                break;
 +
 +            case UNASSIGN:
 +                updated = getAnyLogic().unassign(key, CollectionWrapper.unwrap(resourceNames));
 +                break;
 +
 +            case DEPROVISION:
 +                updated = getAnyLogic().deprovision(key, CollectionWrapper.unwrap(resourceNames));
 +                break;
 +
 +            default:
 +                updated = getAnyLogic().read(key);
 +        }
 +
 +        BulkActionResult result = new BulkActionResult();
 +
 +        if (type == ResourceDeassociationActionType.UNLINK) {
 +            for (ResourceKey resourceName : resourceNames) {
 +                result.getResults().put(resourceName.getElement(),
 +                        updated.getResources().contains(resourceName.getElement())
 +                                ? BulkActionResult.Status.FAILURE
 +                                : BulkActionResult.Status.SUCCESS);
 +            }
 +        } else {
 +            for (PropagationStatus propagationStatusTO : updated.getPropagationStatusTOs()) {
 +                result.getResults().put(propagationStatusTO.getResource(),
 +                        BulkActionResult.Status.valueOf(propagationStatusTO.getStatus().toString()));
 +            }
 +        }
 +
 +        return modificationResponse(result);
 +    }
 +
 +    @Override
 +    public Response bulkAssociation(
 +            final Long key, final ResourceAssociationActionType type, final ResourceAssociationMod associationMod) {
 +
 +        TO any = getAnyLogic().read(key);
 +
 +        checkETag(any.getETagValue());
 +
 +        TO updated;
 +        switch (type) {
 +            case LINK:
 +                updated = getAnyLogic().link(
 +                        key,
 +                        CollectionWrapper.unwrap(associationMod.getTargetResources()));
 +                break;
 +
 +            case ASSIGN:
 +                updated = getAnyLogic().assign(
 +                        key,
 +                        CollectionWrapper.unwrap(associationMod.getTargetResources()),
 +                        associationMod.isChangePwd(),
 +                        associationMod.getPassword());
 +                break;
 +
 +            case PROVISION:
 +                updated = getAnyLogic().provision(
 +                        key,
 +                        CollectionWrapper.unwrap(associationMod.getTargetResources()),
 +                        associationMod.isChangePwd(),
 +                        associationMod.getPassword());
 +                break;
 +
 +            default:
 +                updated = getAnyLogic().read(key);
 +        }
 +
 +        BulkActionResult result = new BulkActionResult();
 +
 +        if (type == ResourceAssociationActionType.LINK) {
 +            for (ResourceKey resourceName : associationMod.getTargetResources()) {
 +                result.getResults().put(resourceName.getElement(),
 +                        updated.getResources().contains(resourceName.getElement())
 +                                ? BulkActionResult.Status.FAILURE
 +                                : BulkActionResult.Status.SUCCESS);
 +            }
 +        } else {
 +            for (PropagationStatus propagationStatusTO : updated.getPropagationStatusTOs()) {
 +                result.getResults().put(propagationStatusTO.getResource(),
 +                        BulkActionResult.Status.valueOf(propagationStatusTO.getStatus().toString()));
 +            }
 +        }
 +
 +        return modificationResponse(result);
 +    }
 +
 +    @Override
 +    public BulkActionResult bulk(final BulkAction bulkAction) {
 +        AbstractAnyLogic<TO, MOD> logic = getAnyLogic();
 +
 +        BulkActionResult result = new BulkActionResult();
 +
 +        switch (bulkAction.getOperation()) {
 +            case DELETE:
 +                for (String key : bulkAction.getTargets()) {
 +                    try {
 +                        result.getResults().put(
 +                                String.valueOf(logic.delete(Long.valueOf(key)).getKey()),
 +                                BulkActionResult.Status.SUCCESS);
 +                    } catch (Exception e) {
 +                        LOG.error("Error performing delete for user {}", key, e);
 +                        result.getResults().put(key, BulkActionResult.Status.FAILURE);
 +                    }
 +                }
 +                break;
 +
 +            case SUSPEND:
 +                if (logic instanceof UserLogic) {
 +                    for (String key : bulkAction.getTargets()) {
 +                        StatusMod statusMod = new StatusMod();
 +                        statusMod.setKey(Long.valueOf(key));
 +                        statusMod.setType(StatusMod.ModType.SUSPEND);
 +                        try {
 +                            result.getResults().put(
 +                                    String.valueOf(((UserLogic) logic).status(statusMod).getKey()),
 +                                    BulkActionResult.Status.SUCCESS);
 +                        } catch (Exception e) {
 +                            LOG.error("Error performing suspend for user {}", key, e);
 +                            result.getResults().put(key, BulkActionResult.Status.FAILURE);
 +                        }
 +                    }
 +                }
 +                break;
 +
 +            case REACTIVATE:
 +                for (String key : bulkAction.getTargets()) {
 +                    StatusMod statusMod = new StatusMod();
 +                    statusMod.setKey(Long.valueOf(key));
 +                    statusMod.setType(StatusMod.ModType.REACTIVATE);
 +                    try {
 +                        result.getResults().put(
 +                                String.valueOf(((UserLogic) logic).status(statusMod).getKey()),
 +                                BulkActionResult.Status.SUCCESS);
 +                    } catch (Exception e) {
 +                        LOG.error("Error performing reactivate for user {}", key, e);
 +                        result.getResults().put(key, BulkActionResult.Status.FAILURE);
 +                    }
 +                }
 +                break;
 +
 +            default:
 +        }
 +
 +        return result;
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
----------------------------------------------------------------------
diff --cc core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
index a594bac,0000000..9265bcb
mode 100644,000000..100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
@@@ -1,224 -1,0 +1,221 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.rest.cxf.service;
 +
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.List;
 +import java.util.Map;
 +import javax.ws.rs.core.Context;
 +import javax.ws.rs.core.EntityTag;
 +import javax.ws.rs.core.MultivaluedMap;
 +import javax.ws.rs.core.Response;
 +import javax.ws.rs.core.UriBuilder;
 +import javax.ws.rs.core.UriInfo;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.cxf.jaxrs.ext.MessageContext;
 +import org.apache.cxf.jaxrs.ext.search.SearchBean;
 +import org.apache.cxf.jaxrs.ext.search.SearchCondition;
 +import org.apache.cxf.jaxrs.ext.search.SearchContext;
 +import org.apache.syncope.common.lib.AbstractBaseBean;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +import org.apache.syncope.common.lib.to.PagedResult;
 +import org.apache.syncope.common.lib.types.ClientExceptionType;
 +import org.apache.syncope.common.rest.api.service.JAXRSService;
 +import org.apache.syncope.common.rest.api.Preference;
 +import org.apache.syncope.common.rest.api.RESTHeaders;
 +import org.apache.syncope.core.misc.search.SearchCondVisitor;
 +import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 +import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +
 +abstract class AbstractServiceImpl implements JAXRSService {
 +
-     /**
-      * Logger.
-      */
 +    protected static final Logger LOG = LoggerFactory.getLogger(AbstractServiceImpl.class);
 +
 +    protected static final String OPTIONS_ALLOW = "GET,POST,OPTIONS,HEAD";
 +
 +    @Context
 +    protected UriInfo uriInfo;
 +
 +    @Context
 +    protected MessageContext messageContext;
 +
 +    @Context
 +    protected SearchContext searchContext;
 +
 +    /**
 +     * Reads <tt>Prefer</tt> header from request and parses into a <tt>Preference</tt> instance.
 +     *
 +     * @return a <tt>Preference</tt> instance matching the passed <tt>Prefer</tt> header,
 +     * or <tt>Preference.NONE</tt> if missing.
 +     */
 +    protected Preference getPreference() {
 +        return Preference.fromString(messageContext.getHttpHeaders().getHeaderString(RESTHeaders.PREFER));
 +    }
 +
 +    /**
 +     * Builds response to successful <tt>create</tt> request, taking into account any <tt>Prefer</tt> header.
 +     *
 +     * @param id identifier of the created entity
 +     * @param entity the entity just created
 +     * @return response to successful <tt>create</tt> request
 +     */
 +    protected Response createResponse(final Object id, final Object entity) {
 +        Response.ResponseBuilder builder = Response.
 +                created(uriInfo.getAbsolutePathBuilder().path(String.valueOf(id)).build()).
 +                header(RESTHeaders.RESOURCE_KEY, id);
 +
 +        switch (getPreference()) {
 +            case RETURN_NO_CONTENT:
 +                break;
 +
 +            case RETURN_CONTENT:
 +            case NONE:
 +            default:
 +                builder = builder.entity(entity);
 +                break;
 +
 +        }
 +        if (getPreference() == Preference.RETURN_CONTENT || getPreference() == Preference.RETURN_NO_CONTENT) {
 +            builder = builder.header(RESTHeaders.PREFERENCE_APPLIED, getPreference().toString());
 +        }
 +
 +        return builder.build();
 +    }
 +
 +    /**
 +     * Builds response to successful modification request, taking into account any <tt>Prefer</tt> header.
 +     *
 +     * @param entity the entity just modified
 +     * @return response to successful modification request
 +     */
 +    protected Response modificationResponse(final Object entity) {
 +        Response.ResponseBuilder builder;
 +        switch (getPreference()) {
 +            case RETURN_NO_CONTENT:
 +                builder = Response.noContent();
 +                break;
 +
 +            case RETURN_CONTENT:
 +            case NONE:
 +            default:
 +                builder = Response.ok(entity);
 +                break;
 +        }
 +        if (getPreference() == Preference.RETURN_CONTENT || getPreference() == Preference.RETURN_NO_CONTENT) {
 +            builder = builder.header(RESTHeaders.PREFERENCE_APPLIED, getPreference().toString());
 +        }
 +
 +        return builder.build();
 +    }
 +
 +    protected void checkETag(final String etag) {
 +        Response.ResponseBuilder builder = messageContext.getRequest().evaluatePreconditions(new EntityTag(etag));
 +        if (builder != null) {
 +            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.ConcurrentModification);
 +            sce.getElements().add("Mismatching ETag value");
 +            throw sce;
 +        }
 +    }
 +
 +    protected SearchCond getSearchCond(final String fiql) {
 +        try {
 +            SearchCondVisitor visitor = new SearchCondVisitor();
 +            SearchCondition<SearchBean> sc = searchContext.getCondition(fiql, SearchBean.class);
 +            sc.accept(visitor);
 +
 +            return visitor.getQuery();
 +        } catch (Exception e) {
 +            LOG.error("Invalid FIQL expression: {}", fiql, e);
 +
 +            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchExpression);
 +            sce.getElements().add(fiql);
 +            throw sce;
 +        }
 +    }
 +
 +    protected List<OrderByClause> getOrderByClauses(final String orderBy) {
 +        if (StringUtils.isBlank(orderBy)) {
 +            return Collections.<OrderByClause>emptyList();
 +        }
 +
 +        List<OrderByClause> result = new ArrayList<>();
 +
 +        for (String clause : orderBy.split(",")) {
 +            String[] elems = clause.split(" ");
 +
 +            if (elems.length > 0 && StringUtils.isNotBlank(elems[0])) {
 +                OrderByClause obc = new OrderByClause();
 +                obc.setField(elems[0].trim());
 +                if (elems.length > 1 && StringUtils.isNotBlank(elems[1])) {
 +                    obc.setDirection(elems[1].trim().equalsIgnoreCase(OrderByClause.Direction.ASC.name())
 +                            ? OrderByClause.Direction.ASC : OrderByClause.Direction.DESC);
 +                }
 +                result.add(obc);
 +            }
 +        }
 +
 +        return result;
 +    }
 +
 +    /**
 +     * Builds a paged result out of a list of items and additional information.
 +     *
 +     * @param <T> result type
 +     * @param list bare list of items to be returned
 +     * @param page current page
 +     * @param size requested size
 +     * @param totalCount total result size (not considering pagination)
 +     * @return paged result
 +     */
 +    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<>();
 +        result.getResult().addAll(list);
 +
 +        result.setPage(page);
 +        result.setSize(result.getResult().size());
 +        result.setTotalCount(totalCount);
 +
 +        UriBuilder builder = uriInfo.getAbsolutePathBuilder();
 +        MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
 +        for (Map.Entry<String, List<String>> queryParam : queryParams.entrySet()) {
 +            builder = builder.queryParam(queryParam.getKey(), queryParam.getValue().toArray());
 +        }
 +
 +        if (result.getPage() > 1) {
 +            result.setPrev(builder.
 +                    replaceQueryParam(PARAM_PAGE, result.getPage() - 1).
 +                    replaceQueryParam(PARAM_SIZE, size).
 +                    build());
 +        }
 +        if ((result.getPage() - 1) * size + result.getSize() < totalCount) {
 +            result.setNext(builder.
 +                    replaceQueryParam(PARAM_PAGE, result.getPage() + 1).
 +                    replaceQueryParam(PARAM_SIZE, size).
 +                    build());
 +        }
 +
 +        return result;
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java
----------------------------------------------------------------------
diff --cc core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java
index ce6390a,0000000..859d008
mode 100644,000000..100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java
@@@ -1,71 -1,0 +1,72 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.rest.cxf.service;
 +
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Transformer;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.syncope.common.lib.SyncopeConstants;
 +import org.apache.syncope.common.lib.mod.AnyObjectMod;
 +import org.apache.syncope.common.lib.to.AnyObjectTO;
 +import org.apache.syncope.common.lib.to.PagedResult;
 +import org.apache.syncope.common.rest.api.beans.AnyListQuery;
 +import org.apache.syncope.common.rest.api.service.AnyObjectService;
 +import org.apache.syncope.core.logic.AbstractAnyLogic;
 +import org.apache.syncope.core.logic.AnyObjectLogic;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.stereotype.Service;
 +
 +@Service
 +public class AnyObjectServiceImpl extends AbstractAnyService<AnyObjectTO, AnyObjectMod> implements AnyObjectService {
 +
 +    @Autowired
 +    private AnyObjectLogic logic;
 +
 +    @Override
 +    protected AbstractAnyLogic<AnyObjectTO, AnyObjectMod> getAnyLogic() {
 +        return logic;
 +    }
 +
 +    @Override
 +    public PagedResult<AnyObjectTO> list(final String type, final AnyListQuery listQuery) {
 +        if (StringUtils.isBlank(type)) {
 +            return super.list(listQuery);
 +        }
 +
 +        CollectionUtils.transform(listQuery.getRealms(), new Transformer<String, String>() {
 +
 +            @Override
 +            public String transform(final String input) {
 +                return StringUtils.prependIfMissing(input, SyncopeConstants.ROOT_REALM);
 +            }
 +        });
 +
 +        return buildPagedResult(
 +                logic.list(
 +                        type,
 +                        listQuery.getPage(),
 +                        listQuery.getSize(),
 +                        getOrderByClauses(listQuery.getOrderBy()),
-                         listQuery.getRealms()),
++                        listQuery.getRealms(),
++                        listQuery.isDetails()),
 +                listQuery.getPage(),
 +                listQuery.getSize(),
 +                getAnyLogic().count(listQuery.getRealms()));
 +    }
 +}


[5/7] syncope git commit: [SYNCOPE-676] Merge from 1_2_X

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/logic/src/test/java/org/apache/syncope/core/logic/NotificationTest.java
----------------------------------------------------------------------
diff --cc core/logic/src/test/java/org/apache/syncope/core/logic/NotificationTest.java
index bc425dc,0000000..c5dc13c
mode 100644,000000..100644
--- a/core/logic/src/test/java/org/apache/syncope/core/logic/NotificationTest.java
+++ b/core/logic/src/test/java/org/apache/syncope/core/logic/NotificationTest.java
@@@ -1,656 -1,0 +1,657 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.logic;
 +
 +import static org.junit.Assert.assertEquals;
 +import static org.junit.Assert.assertNotNull;
 +import static org.junit.Assert.assertTrue;
 +
 +import com.icegreen.greenmail.util.GreenMail;
 +import com.icegreen.greenmail.util.ServerSetup;
 +import java.text.SimpleDateFormat;
 +import java.util.ArrayList;
 +import java.util.Date;
 +import java.util.List;
 +import java.util.Random;
 +import java.util.Set;
 +import java.util.UUID;
 +import javax.annotation.Resource;
 +import javax.mail.Flags.Flag;
 +import javax.mail.Folder;
 +import javax.mail.Message;
 +import javax.mail.Session;
 +import javax.mail.Store;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Transformer;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.syncope.common.lib.SyncopeConstants;
 +import org.apache.syncope.common.lib.search.GroupFiqlSearchConditionBuilder;
 +import org.apache.syncope.common.lib.search.UserFiqlSearchConditionBuilder;
 +import org.apache.syncope.common.lib.to.AttrTO;
 +import org.apache.syncope.common.lib.to.MembershipTO;
 +import org.apache.syncope.common.lib.to.NotificationTaskTO;
 +import org.apache.syncope.common.lib.to.GroupTO;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.Entitlement;
 +import org.apache.syncope.common.lib.types.IntMappingType;
 +import org.apache.syncope.common.lib.types.TaskType;
 +import org.apache.syncope.common.lib.types.TraceLevel;
 +import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 +import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
 +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 +import org.apache.syncope.core.persistence.api.dao.TaskDAO;
 +import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 +import org.apache.syncope.core.persistence.api.entity.Notification;
 +import org.apache.syncope.core.persistence.api.entity.conf.CPlainAttr;
 +import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
 +import org.apache.syncope.core.logic.notification.NotificationJob;
 +import org.apache.syncope.core.misc.security.SyncopeGrantedAuthority;
 +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 +import org.apache.syncope.core.persistence.api.entity.AnyAbout;
 +import org.apache.syncope.core.persistence.api.entity.conf.CPlainAttrValue;
 +import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 +import org.junit.AfterClass;
 +import org.junit.Before;
 +import org.junit.BeforeClass;
 +import org.junit.Test;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.mail.javamail.JavaMailSender;
 +import org.springframework.mail.javamail.JavaMailSenderImpl;
 +import org.springframework.security.authentication.TestingAuthenticationToken;
 +import org.springframework.security.core.Authentication;
 +import org.springframework.security.core.GrantedAuthority;
 +import org.springframework.security.core.context.SecurityContextHolder;
 +import org.springframework.security.core.userdetails.User;
 +import org.springframework.security.core.userdetails.UserDetails;
 +
 +public class NotificationTest extends AbstractTest {
 +
 +    private static final String SMTP_HOST = "localhost";
 +
 +    private static final int SMTP_PORT = 2525;
 +
 +    private static final String POP3_HOST = "localhost";
 +
 +    private static final int POP3_PORT = 1110;
 +
 +    private static final String MAIL_ADDRESS = "notificationtest@syncope.apache.org";
 +
 +    private static final String MAIL_PASSWORD = "password";
 +
 +    private static GreenMail greenMail;
 +
 +    @Resource(name = "adminUser")
 +    private String adminUser;
 +
 +    @Autowired
 +    private NotificationDAO notificationDAO;
 +
 +    @Autowired
 +    private AnyTypeDAO anyTypeDAO;
 +
 +    @Autowired
 +    private TaskDAO taskDAO;
 +
 +    @Autowired
 +    private PlainSchemaDAO plainSchemaDAO;
 +
 +    @Autowired
 +    private ConfDAO confDAO;
 +
 +    @Autowired
 +    private UserLogic userLogic;
 +
 +    @Autowired
 +    private GroupLogic groupLogic;
 +
 +    @Autowired
 +    private TaskLogic taskLogic;
 +
 +    @Autowired
 +    private NotificationJob notificationJob;
 +
 +    @Autowired
 +    private NotificationManager notificationManager;
 +
 +    @Autowired
 +    private JavaMailSender mailSender;
 +
 +    @Autowired
 +    private EntityFactory entityFactory;
 +
 +    @BeforeClass
 +    public static void startGreenMail() {
 +        ServerSetup[] config = new ServerSetup[2];
 +        config[0] = new ServerSetup(SMTP_PORT, SMTP_HOST, ServerSetup.PROTOCOL_SMTP);
 +        config[1] = new ServerSetup(POP3_PORT, POP3_HOST, ServerSetup.PROTOCOL_POP3);
 +        greenMail = new GreenMail(config);
 +        greenMail.setUser(MAIL_ADDRESS, MAIL_PASSWORD);
 +        greenMail.start();
 +    }
 +
 +    @AfterClass
 +    public static void stopGreenMail() {
 +        if (greenMail != null) {
 +            greenMail.stop();
 +        }
 +    }
 +
 +    private static UserTO getUniqueSampleTO(final String email) {
 +        return getSampleTO(UUID.randomUUID().toString().substring(0, 8) + email);
 +    }
 +
 +    private static AttrTO attributeTO(final String schema, final String value) {
 +        AttrTO attr = new AttrTO();
 +        attr.setSchema(schema);
 +        attr.getValues().add(value);
 +        return attr;
 +    }
 +
 +    private static UserTO getSampleTO(final String email) {
 +        String uid = email;
 +        UserTO userTO = new UserTO();
 +        userTO.setPassword("password123");
 +        userTO.setUsername(uid);
 +        userTO.setRealm("/even/two");
 +
 +        userTO.getPlainAttrs().add(attributeTO("fullname", uid));
 +        userTO.getPlainAttrs().add(attributeTO("firstname", uid));
 +        userTO.getPlainAttrs().add(attributeTO("surname", "surname"));
 +        userTO.getPlainAttrs().add(attributeTO("type", "a type"));
 +        userTO.getPlainAttrs().add(attributeTO("userId", uid));
 +        userTO.getPlainAttrs().add(attributeTO("email", uid));
 +        userTO.getPlainAttrs().add(attributeTO("loginDate", new SimpleDateFormat("yyyy-MM-dd").format(new Date())));
 +        userTO.getDerAttrs().add(attributeTO("cn", null));
 +        userTO.getVirAttrs().add(attributeTO("virtualdata", "virtualvalue"));
 +        return userTO;
 +    }
 +
 +    @Before
 +    public void setupSecurity() {
 +        List<GrantedAuthority> authorities = CollectionUtils.collect(Entitlement.values(),
 +                new Transformer<String, GrantedAuthority>() {
 +
 +                    @Override
 +                    public GrantedAuthority transform(final String entitlement) {
 +                        return new SyncopeGrantedAuthority(entitlement, SyncopeConstants.ROOT_REALM);
 +                    }
 +                }, new ArrayList<GrantedAuthority>());
 +
 +        UserDetails userDetails = new User(adminUser, "FAKE_PASSWORD", authorities);
 +        Authentication authentication = new TestingAuthenticationToken(userDetails, "FAKE_PASSWORD", authorities);
 +        SecurityContextHolder.getContext().setAuthentication(authentication);
 +    }
 +
 +    @Before
 +    public void setupSMTP() throws Exception {
 +        JavaMailSenderImpl sender = (JavaMailSenderImpl) mailSender;
 +        sender.setDefaultEncoding(SyncopeConstants.DEFAULT_ENCODING);
 +        sender.setHost(SMTP_HOST);
 +        sender.setPort(SMTP_PORT);
 +    }
 +
 +    private boolean verifyMail(final String sender, final String subject) throws Exception {
 +        LOG.info("Waiting for notification to be sent...");
 +        try {
 +            Thread.sleep(1000);
 +        } catch (InterruptedException e) {
 +        }
 +
 +        boolean found = false;
 +        Session session = Session.getDefaultInstance(System.getProperties());
 +        Store store = session.getStore("pop3");
 +        store.connect(POP3_HOST, POP3_PORT, MAIL_ADDRESS, MAIL_PASSWORD);
 +
 +        Folder inbox = store.getFolder("INBOX");
 +        assertNotNull(inbox);
 +        inbox.open(Folder.READ_WRITE);
 +
 +        Message[] messages = inbox.getMessages();
-         for (int i = 0; i < messages.length; i++) {
-             if (sender.equals(messages[i].getFrom()[0].toString()) && subject.equals(messages[i].getSubject())) {
++        for (Message message : messages) {
++            if (sender.equals(message.getFrom()[0].toString()) && subject.equals(message.getSubject())) {
 +                found = true;
-                 messages[i].setFlag(Flag.DELETED, true);
++                message.setFlag(Flag.DELETED, true);
 +            }
 +        }
 +
 +        inbox.close(true);
 +        store.close();
 +        return found;
 +    }
 +
 +    @Test
 +    public void notifyByMail() throws Exception {
 +        // 1. create suitable notification for subsequent tests
 +        Notification notification = entityFactory.newEntity(Notification.class);
 +        notification.getEvents().add("[REST]:[UserLogic]:[]:[create]:[SUCCESS]");
 +
 +        AnyAbout about = entityFactory.newEntity(AnyAbout.class);
 +        about.setNotification(notification);
 +        notification.add(about);
 +        about.setAnyType(anyTypeDAO.findUser());
 +        about.set(new UserFiqlSearchConditionBuilder().inGroups(7L).query());
 +
 +        notification.setRecipients(new UserFiqlSearchConditionBuilder().inGroups(8L).query());
 +        notification.setSelfAsRecipient(true);
 +
 +        notification.setRecipientAttrName("email");
 +        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 +
 +        Random random = new Random(System.currentTimeMillis());
 +        String sender = "syncopetest-" + random.nextLong() + "@syncope.apache.org";
 +        notification.setSender(sender);
 +        String subject = "Test notification " + random.nextLong();
 +        notification.setSubject(subject);
 +        notification.setTemplate("optin");
 +
 +        Notification actual = notificationDAO.save(notification);
 +        assertNotNull(actual);
 +
 +        notificationDAO.flush();
 +
 +        // 2. create user
 +        UserTO userTO = getSampleTO(MAIL_ADDRESS);
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(7);
 +        userTO.getMemberships().add(membershipTO);
 +
 +        userLogic.create(userTO, true);
 +
 +        // 3. force Quartz job execution and verify e-mail
 +        notificationJob.execute(null);
 +        assertTrue(verifyMail(sender, subject));
 +
 +        // 4. get NotificationTask id and text body
 +        Long taskId = null;
 +        String textBody = null;
 +        for (NotificationTask task : taskDAO.<NotificationTask>findAll(TaskType.NOTIFICATION)) {
 +            if (sender.equals(task.getSender())) {
 +                taskId = task.getKey();
 +                textBody = task.getTextBody();
 +            }
 +        }
 +        assertNotNull(taskId);
 +        assertNotNull(textBody);
 +        assertTrue("Notification mail text doesn't contain expected content.",
 +                textBody.contains("Your email address is notificationtest@syncope.apache.org."));
 +        assertTrue("Notification mail text doesn't contain expected content.",
 +                textBody.contains("Your email address inside a link: "
 +                        + "http://localhost/?email=notificationtest%40syncope.apache.org ."));
 +
 +        // 5. execute Notification task and verify e-mail
 +        taskLogic.execute(taskId, false);
 +        assertTrue(verifyMail(sender, subject));
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE192() throws Exception {
 +        // 1. create suitable notification for subsequent tests
 +        Notification notification = entityFactory.newEntity(Notification.class);
 +        notification.getEvents().add("[REST]:[UserLogic]:[]:[create]:[SUCCESS]");
 +
 +        AnyAbout about = entityFactory.newEntity(AnyAbout.class);
 +        about.setNotification(notification);
 +        notification.add(about);
 +        about.setAnyType(anyTypeDAO.findUser());
 +        about.set(new UserFiqlSearchConditionBuilder().inGroups(7L).query());
 +
 +        notification.setRecipients(new UserFiqlSearchConditionBuilder().inGroups(8L).query());
 +        notification.setSelfAsRecipient(true);
 +
 +        notification.setRecipientAttrName("email");
 +        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 +
 +        Random random = new Random(System.currentTimeMillis());
 +        String sender = "syncope192-" + random.nextLong() + "@syncope.apache.org";
 +        notification.setSender(sender);
 +        String subject = "Test notification " + random.nextLong();
 +        notification.setSubject(subject);
 +        notification.setTemplate("optin");
 +        notification.setTraceLevel(TraceLevel.NONE);
 +
 +        Notification actual = notificationDAO.save(notification);
 +        assertNotNull(actual);
 +
 +        // 2. create user
 +        UserTO userTO = getSampleTO(MAIL_ADDRESS);
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(7);
 +        userTO.getMemberships().add(membershipTO);
 +
 +        userLogic.create(userTO, true);
 +
 +        // 3. force Quartz job execution and verify e-mail
 +        notificationJob.execute(null);
 +        assertTrue(verifyMail(sender, subject));
 +
 +        // 4. get NotificationTask id
 +        Long taskId = null;
 +        for (NotificationTask task : taskDAO.<NotificationTask>findAll(TaskType.NOTIFICATION)) {
 +            if (sender.equals(task.getSender())) {
 +                taskId = task.getKey();
 +            }
 +        }
 +        assertNotNull(taskId);
 +
 +        // 5. verify that last exec status was updated
 +        NotificationTaskTO task = (NotificationTaskTO) taskLogic.read(taskId);
 +        assertNotNull(task);
 +        assertTrue(task.getExecutions().isEmpty());
 +        assertTrue(task.isExecuted());
 +        assertTrue(StringUtils.isNotBlank(task.getLatestExecStatus()));
 +    }
 +
 +    @Test
 +    public void notifyByMailEmptyAbout() throws Exception {
 +        // 1. create suitable notification for subsequent tests
 +        Notification notification = entityFactory.newEntity(Notification.class);
 +        notification.getEvents().add("[REST]:[UserLogic]:[]:[create]:[SUCCESS]");
 +        notification.setRecipients(new UserFiqlSearchConditionBuilder().inGroups(8L).query());
 +        notification.setSelfAsRecipient(true);
 +
 +        notification.setRecipientAttrName("email");
 +        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 +
 +        Random random = new Random(System.currentTimeMillis());
 +        String sender = "syncopetest-" + random.nextLong() + "@syncope.apache.org";
 +        notification.setSender(sender);
 +        String subject = "Test notification " + random.nextLong();
 +        notification.setSubject(subject);
 +        notification.setTemplate("optin");
 +
 +        Notification actual = notificationDAO.save(notification);
 +        assertNotNull(actual);
 +
 +        notificationDAO.flush();
 +
 +        // 2. create user
 +        UserTO userTO = getSampleTO(MAIL_ADDRESS);
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(7);
 +        userTO.getMemberships().add(membershipTO);
 +
 +        userLogic.create(userTO, true);
 +
 +        // 3. force Quartz job execution and verify e-mail
 +        notificationJob.execute(null);
 +        assertTrue(verifyMail(sender, subject));
 +
 +        // 4. get NotificationTask id
 +        Long taskId = null;
 +        for (NotificationTask task : taskDAO.<NotificationTask>findAll(TaskType.NOTIFICATION)) {
 +            if (sender.equals(task.getSender())) {
 +                taskId = task.getKey();
 +            }
 +        }
 +        assertNotNull(taskId);
 +
 +        // 5. execute Notification task and verify e-mail
 +        taskLogic.execute(taskId, false);
 +        assertTrue(verifyMail(sender, subject));
 +    }
 +
 +    @Test
 +    public void notifyByMailWithRetry() throws Exception {
 +        // 1. create suitable notification for subsequent tests
 +        Notification notification = entityFactory.newEntity(Notification.class);
 +        notification.getEvents().add("[REST]:[UserLogic]:[]:[create]:[SUCCESS]");
 +        notification.setRecipients(new UserFiqlSearchConditionBuilder().inGroups(8L).query());
 +        notification.setSelfAsRecipient(true);
 +
 +        notification.setRecipientAttrName("email");
 +        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 +
 +        Random random = new Random(System.currentTimeMillis());
 +        String sender = "syncopetest-" + random.nextLong() + "@syncope.apache.org";
 +        notification.setSender(sender);
 +        String subject = "Test notification " + random.nextLong();
 +        notification.setSubject(subject);
 +        notification.setTemplate("optin");
 +
 +        Notification actual = notificationDAO.save(notification);
 +        assertNotNull(actual);
 +
 +        notificationDAO.flush();
 +
 +        // 2. create user
 +        UserTO userTO = getSampleTO(MAIL_ADDRESS);
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(7);
 +        userTO.getMemberships().add(membershipTO);
 +
 +        userLogic.create(userTO, true);
 +
 +        // 3. Set number of retries
 +        CPlainAttr maxRetries = entityFactory.newEntity(CPlainAttr.class);
 +        maxRetries.setSchema(plainSchemaDAO.find("notification.maxRetries"));
 +        CPlainAttrValue maxRetriesValue = entityFactory.newEntity(CPlainAttrValue.class);
 +        maxRetries.add("5", maxRetriesValue);
 +        confDAO.save(maxRetries);
 +        confDAO.flush();
 +
 +        // 4. Stop mail server to force error sending mail
 +        stopGreenMail();
 +
 +        // 5. force Quartz job execution multiple times
 +        for (int i = 0; i < 10; i++) {
 +            notificationJob.execute(null);
 +        }
 +
 +        // 6. get NotificationTask, count number of executions
 +        NotificationTask foundTask = null;
 +        for (NotificationTask task : taskDAO.<NotificationTask>findAll(TaskType.NOTIFICATION)) {
 +            if (sender.equals(task.getSender())) {
 +                foundTask = task;
 +            }
 +        }
 +        assertNotNull(foundTask);
 +        assertEquals(6, notificationManager.countExecutionsWithStatus(foundTask.getKey(),
 +                NotificationJob.Status.NOT_SENT.name()));
 +
 +        // 7. start mail server again
 +        startGreenMail();
 +
 +        // 8. reset number of retries
 +        maxRetries = entityFactory.newEntity(CPlainAttr.class);
 +        maxRetries.setSchema(plainSchemaDAO.find("notification.maxRetries"));
 +        maxRetriesValue = entityFactory.newEntity(CPlainAttrValue.class);
 +        maxRetries.add("0", maxRetriesValue);
 +        confDAO.save(maxRetries);
 +        confDAO.flush();
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE445() throws Exception {
 +        // 1. create suitable notification for subsequent tests
 +        Notification notification = entityFactory.newEntity(Notification.class);
 +        notification.getEvents().add("[REST]:[UserLogic]:[]:[create]:[SUCCESS]");
 +
 +        AnyAbout about = entityFactory.newEntity(AnyAbout.class);
 +        about.setNotification(notification);
 +        notification.add(about);
 +        about.setAnyType(anyTypeDAO.findUser());
 +        about.set(new UserFiqlSearchConditionBuilder().inGroups(7L).query());
 +
 +        notification.setRecipients(new UserFiqlSearchConditionBuilder().inGroups(8L).query());
 +        notification.setSelfAsRecipient(true);
 +
 +        notification.setRecipientAttrName("email");
 +        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 +
 +        notification.getStaticRecipients().add("syncope445@syncope.apache.org");
 +
 +        Random random = new Random(System.currentTimeMillis());
 +        String sender = "syncopetest-" + random.nextLong() + "@syncope.apache.org";
 +        notification.setSender(sender);
 +        String subject = "Test notification " + random.nextLong();
 +        notification.setSubject(subject);
 +        notification.setTemplate("optin");
 +
 +        Notification actual = notificationDAO.save(notification);
 +        assertNotNull(actual);
 +
 +        notificationDAO.flush();
 +
 +        // 2. create user
 +        UserTO userTO = getSampleTO(MAIL_ADDRESS);
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(7);
 +        userTO.getMemberships().add(membershipTO);
 +
 +        userLogic.create(userTO, true);
 +
 +        // 3. force Quartz job execution and verify e-mail
 +        notificationJob.execute(null);
 +        assertTrue(verifyMail(sender, subject));
 +
 +        // 4. get NotificationTask id and text body
 +        Long taskId = null;
 +        String textBody = null;
 +        Set<String> recipients = null;
 +        for (NotificationTask task : taskDAO.<NotificationTask>findAll(TaskType.NOTIFICATION)) {
 +            if (sender.equals(task.getSender())) {
 +                taskId = task.getKey();
 +                textBody = task.getTextBody();
 +                recipients = task.getRecipients();
 +            }
 +        }
 +
 +        assertNotNull(taskId);
 +        assertNotNull(textBody);
++        assertNotNull(recipients);
 +        assertTrue(recipients.contains("syncope445@syncope.apache.org"));
 +
 +        // 5. execute Notification task and verify e-mail
 +        taskLogic.execute(taskId, false);
 +        assertTrue(verifyMail(sender, subject));
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE492() throws Exception {
 +        // 1. create suitable disabled notification for subsequent tests
 +        Notification notification = entityFactory.newEntity(Notification.class);
 +        notification.getEvents().add("[REST]:[UserLogic]:[]:[create]:[SUCCESS]");
 +
 +        AnyAbout about = entityFactory.newEntity(AnyAbout.class);
 +        about.setNotification(notification);
 +        notification.add(about);
 +        about.setAnyType(anyTypeDAO.findUser());
 +        about.set(new UserFiqlSearchConditionBuilder().inGroups(7L).query());
 +
 +        notification.setSelfAsRecipient(true);
 +
 +        notification.setRecipientAttrName("email");
 +        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 +
 +        notification.getStaticRecipients().add("syncope492@syncope.apache.org");
 +
 +        Random random = new Random(System.currentTimeMillis());
 +        String sender = "syncopetest-" + random.nextLong() + "@syncope.apache.org";
 +        notification.setSender(sender);
 +        String subject = "Test notification " + random.nextLong();
 +        notification.setSubject(subject);
 +        notification.setTemplate("optin");
 +        notification.setActive(false);
 +
 +        Notification actual = notificationDAO.save(notification);
 +        assertNotNull(actual);
 +
 +        notificationDAO.flush();
 +
 +        final int tasksNumberBefore = taskDAO.findAll(TaskType.NOTIFICATION).size();
 +
 +        // 2. create user
 +        UserTO userTO = getUniqueSampleTO(MAIL_ADDRESS);
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(7);
 +        userTO.getMemberships().add(membershipTO);
 +
 +        userLogic.create(userTO, true);
 +
 +        // 3. force Quartz job execution
 +        notificationJob.execute(null);
 +
 +        // 4. check if number of tasks is not incremented
 +        assertEquals(tasksNumberBefore, taskDAO.findAll(TaskType.NOTIFICATION).size());
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE446() throws Exception {
 +        // 1. create suitable notification for subsequent tests
 +        Notification notification = entityFactory.newEntity(Notification.class);
 +        notification.getEvents().add("[REST]:[GroupLogic]:[]:[create]:[SUCCESS]");
 +
 +        AnyAbout about = entityFactory.newEntity(AnyAbout.class);
 +        about.setNotification(notification);
 +        notification.add(about);
 +        about.setAnyType(anyTypeDAO.findGroup());
 +        about.set(new GroupFiqlSearchConditionBuilder().is("name").equalTo("group446").query());
 +
 +        notification.setSelfAsRecipient(false);
 +
 +        notification.setRecipientAttrName("email");
 +        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 +
 +        notification.getStaticRecipients().add(MAIL_ADDRESS);
 +
 +        Random random = new Random(System.currentTimeMillis());
 +        String sender = "syncopetest-" + random.nextLong() + "@syncope.apache.org";
 +        notification.setSender(sender);
 +        String subject = "Test notification " + random.nextLong();
 +        notification.setSubject(subject);
 +        notification.setTemplate("optin");
 +
 +        Notification actual = notificationDAO.save(notification);
 +        assertNotNull(actual);
 +
 +        notificationDAO.flush();
 +
 +        // 2. create group
 +        GroupTO groupTO = new GroupTO();
 +        groupTO.setName("group446");
 +        groupTO.setRealm("/even/two");
 +
 +        GroupTO createdGroup = groupLogic.create(groupTO);
 +        assertNotNull(createdGroup);
 +
 +        // 3. force Quartz job execution and verify e-mail
 +        notificationJob.execute(null);
 +        assertTrue(verifyMail(sender, subject));
 +
 +        // 4. get NotificationTask id and text body
 +        Long taskId = null;
 +        String textBody = null;
 +        Set<String> recipients = null;
 +        for (NotificationTask task : taskDAO.<NotificationTask>findAll(TaskType.NOTIFICATION)) {
 +            if (sender.equals(task.getSender())) {
 +                taskId = task.getKey();
 +                textBody = task.getTextBody();
 +                recipients = task.getRecipients();
 +            }
 +        }
 +
 +        assertNotNull(taskId);
 +        assertNotNull(textBody);
 +        assertTrue(recipients != null && recipients.contains(MAIL_ADDRESS));
 +
 +        // 5. execute Notification task and verify e-mail
 +        taskLogic.execute(taskId, false);
 +        assertTrue(verifyMail(sender, subject));
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/persistence-jpa/src/main/resources/indexes.xml
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/resources/indexes.xml
index ae57529,0000000..ff42b84
mode 100644,000000..100644
--- a/core/persistence-jpa/src/main/resources/indexes.xml
+++ b/core/persistence-jpa/src/main/resources/indexes.xml
@@@ -1,45 -1,0 +1,76 @@@
 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 +<!--
 +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.
 +-->
 +<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
 +<properties>
 +  <comment>Additional indexes (in respect to JPA's)</comment>
 +
 +  <entry key="UPlainAttrValue_stringvalueIndex">CREATE INDEX UAttrValue_stringvalueIndex ON UPlainAttrValue(stringvalue)</entry>
 +  <entry key="UPlainAttrValue_datevalueIndex">CREATE INDEX UAttrValue_datevalueIndex ON UPlainAttrValue(datevalue)</entry>
 +  <entry key="UPlainAttrValue_longvalueIndex">CREATE INDEX UAttrValue_longvalueIndex ON UPlainAttrValue(longvalue)</entry>
 +  <entry key="UPlainAttrValue_doublevalueIndex">CREATE INDEX UAttrValue_doublevalueIndex ON UPlainAttrValue(doublevalue)</entry>
 +  <entry key="UPlainAttrValue_booleanvalueIndex">CREATE INDEX UAttrValue_booleanvalueIndex ON UPlainAttrValue(booleanvalue)</entry>
++
 +  <entry key="APlainAttrValue_stringvalueIndex">CREATE INDEX AAttrValue_stringvalueIndex ON APlainAttrValue(stringvalue)</entry>
 +  <entry key="APlainAttrValue_datevalueIndex">CREATE INDEX AAttrValue_datevalueIndex ON APlainAttrValue(datevalue)</entry>
 +  <entry key="APlainAttrValue_longvalueIndex">CREATE INDEX AAttrValue_longvalueIndex ON APlainAttrValue(longvalue)</entry>
 +  <entry key="APlainAttrValue_doublevalueIndex">CREATE INDEX AAttrValue_doublevalueIndex ON APlainAttrValue(doublevalue)</entry>
 +  <entry key="APlainAttrValue_booleanvalueIndex">CREATE INDEX AAttrValue_booleanvalueIndex ON APlainAttrValue(booleanvalue)</entry>
++
 +  <entry key="GPlainAttrValue_stringvalueIndex">CREATE INDEX GAttrValue_stringvalueIndex ON GPlainAttrValue(stringvalue)</entry>
 +  <entry key="GPlainAttrValue_datevalueIndex">CREATE INDEX GAttrValue_datevalueIndex ON GPlainAttrValue(datevalue)</entry>
 +  <entry key="GPlainAttrValue_longvalueIndex">CREATE INDEX GAttrValue_longvalueIndex ON GPlainAttrValue(longvalue)</entry>
 +  <entry key="GPlainAttrValue_doublevalueIndex">CREATE INDEX GAttrValue_doublevalueIndex ON GPlainAttrValue(doublevalue)</entry>
 +  <entry key="GPlainAttrValue_booleanvalueIndex">CREATE INDEX GAttrValue_booleanvalueIndex ON GPlainAttrValue(booleanvalue)</entry>
++
 +  <entry key="CPlainAttrValue_stringvalueIndex">CREATE INDEX CAttrValue_stringvalueIndex ON CPlainAttrValue(stringvalue)</entry>
 +  <entry key="CPlainAttrValue_datevalueIndex">CREATE INDEX CAttrValue_datevalueIndex ON CPlainAttrValue(datevalue)</entry>
 +  <entry key="CPlainAttrValue_longvalueIndex">CREATE INDEX CAttrValue_longvalueIndex ON CPlainAttrValue(longvalue)</entry>
 +  <entry key="CPlainAttrValue_doublevalueIndex">CREATE INDEX CAttrValue_doublevalueIndex ON CPlainAttrValue(doublevalue)</entry>
 +  <entry key="CPlainAttrValue_booleanvalueIndex">CREATE INDEX CAttrValue_booleanvalueIndex ON CPlainAttrValue(booleanvalue)</entry>
++
++  <entry key="UMembership_GroupIndex">CREATE INDEX UMembership_GroupIndex ON UMembership(group_id)</entry>
++  <entry key="UMembership_UserIndex">CREATE INDEX UMembership_UserIndex ON UMembership(user_id)</entry>
++  <entry key="AMembership_GroupIndex">CREATE INDEX AMembership_GroupIndex ON AMembership(group_id)</entry>
++  <entry key="AMembership_AnyObjectIndex">CREATE INDEX AMembership_AnyObjectIndex ON AMembership(anyObject_id)</entry>
++
++  <entry key="URelationship_RightIndex">CREATE INDEX URelationship_RightIndex ON URelationship(anyObject_id)</entry>
++  <entry key="URelationship_LeftIndex">CREATE INDEX URelationship_LeftIndex ON URelationship(user_id)</entry>
++  <entry key="ARelationship_RightIndex">CREATE INDEX ARelationship_RightIndex ON ARelationship(right_anyObject_id)</entry>
++  <entry key="ARelationship_AnyObjectIndex">CREATE INDEX ARelationship_AnyObjectIndex ON ARelationship(left_anyObject_id)</entry>
++
++  <entry key="UPlainAttrValue_attributeIdIndex">CREATE INDEX UPlainAttrValue_attributeIdIndex on UPlainAttrValue(attribute_id)</entry>
++  <entry key="GPlainAttrValue_attributeIdIndex">CREATE INDEX GPlainAttrValue_attributeIdIndex on GPlainAttrValue(attribute_id)</entry>
++  <entry key="APlainAttrValue_attributeIdIndex">CREATE INDEX APlainAttrValue_attributeIdIndex on APlainAttrValue(attribute_id)</entry>
++  <entry key="CPlainAttrValue_attributeIdIndex">CREATE INDEX CPlainAttrValue_attributeIdIndex on CPlainAttrValue(attribute_id)</entry>
++
++  <entry key="UPlainAttr_owner_id_index">CREATE INDEX UPlainAttr_owner_id_index on UPlainAttr(owner_id)</entry>
++  <entry key="GPlainAttr_owner_id_index">CREATE INDEX GPlainAttr_owner_id_index on GPlainAttr(owner_id)</entry>
++  <entry key="APlainAttr_owner_id_index">CREATE INDEX APlainAttr_owner_id_index on APlainAttr(owner_id)</entry>
++
++  <entry key="UDerAttr_owner_id_index">CREATE INDEX UDerAttr_owner_id_index on UDerAttr(owner_id)</entry>
++  <entry key="GDerAttr_owner_id_index">CREATE INDEX GDerAttr_owner_id_index on GDerAttr(owner_id)</entry>
++  <entry key="ADerAttr_owner_id_index">CREATE INDEX ADerAttr_owner_id_index on ADerAttr(owner_id)</entry>
++
++  <entry key="UVirAttr_owner_id_index">CREATE INDEX UVirAttr_owner_id_index on UVirAttr(owner_id)</entry>
++  <entry key="GVirAttr_owner_id_index">CREATE INDEX GVirAttr_owner_id_index on GVirAttr(owner_id)</entry>
++  <entry key="AVirAttr_owner_id_index">CREATE INDEX AVirAttr_owner_id_index on AVirAttr(owner_id)</entry>
++
 +  <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON Task(executed)</entry>
 +</properties>

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AnyObjectDataBinder.java
----------------------------------------------------------------------
diff --cc core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AnyObjectDataBinder.java
index b503365,0000000..597b639
mode 100644,000000..100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AnyObjectDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AnyObjectDataBinder.java
@@@ -1,35 -1,0 +1,35 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.provisioning.api.data;
 +
 +import org.apache.syncope.common.lib.mod.AnyObjectMod;
 +import org.apache.syncope.common.lib.to.AnyObjectTO;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 +
 +public interface AnyObjectDataBinder {
 +
 +    AnyObjectTO getAnyObjectTO(Long key);
 +
-     AnyObjectTO getAnyObjectTO(AnyObject anyObject);
++    AnyObjectTO getAnyObjectTO(AnyObject anyObject, boolean details);
 +
 +    void create(AnyObject anyObject, AnyObjectTO anyObjectTO);
 +
 +    PropagationByResource update(AnyObject toBeUpdated, AnyObjectMod anyObjectMod);
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/GroupDataBinder.java
----------------------------------------------------------------------
diff --cc core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/GroupDataBinder.java
index 95df28a,0000000..f472aef
mode 100644,000000..100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/GroupDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/GroupDataBinder.java
@@@ -1,36 -1,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.core.provisioning.api.data;
 +
 +import org.apache.syncope.common.lib.mod.GroupMod;
 +import org.apache.syncope.common.lib.to.GroupTO;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.core.persistence.api.entity.group.Group;
 +
 +public interface GroupDataBinder {
 +
 +    GroupTO getGroupTO(Long key);
 +
-     GroupTO getGroupTO(Group group);
++    GroupTO getGroupTO(Group group, boolean details);
 +
 +    Group create(Group group, GroupTO groupTO);
 +
 +    PropagationByResource update(Group group, GroupMod groupMod);
 +
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/UserDataBinder.java
----------------------------------------------------------------------
diff --cc core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/UserDataBinder.java
index 942c212,0000000..fbfe084
mode 100644,000000..100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/UserDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/UserDataBinder.java
@@@ -1,51 -1,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.core.provisioning.api.data;
 +
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +
 +public interface UserDataBinder {
 +
 +    UserTO getAuthenticatedUserTO();
 +
 +    UserTO getUserTO(String username);
 +
 +    UserTO getUserTO(Long key);
 +
-     UserTO getUserTO(User user);
++    UserTO getUserTO(User user, boolean details);
 +
 +    void create(User user, UserTO userTO, boolean storePassword);
 +
 +    /**
 +     * Update user, given UserMod.
 +     *
 +     * @param toBeUpdated user to be updated
 +     * @param userMod bean containing update request
 +     * @return updated user + propagation by resource
 +     * @see PropagationByResource
 +     */
 +    PropagationByResource update(User toBeUpdated, UserMod userMod);
 +
 +    boolean verifyPassword(String username, String password);
 +
 +    boolean verifyPassword(User user, String password);
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
index 237792b,0000000..258e713
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
@@@ -1,292 -1,0 +1,297 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.provisioning.java.data;
 +
 +import static org.apache.syncope.core.provisioning.java.data.AbstractAnyDataBinder.LOG;
 +
 +import java.util.Collection;
 +import java.util.HashSet;
 +import java.util.Map;
 +import java.util.Set;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Transformer;
 +import org.apache.syncope.common.lib.SyncopeClientCompositeException;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +import org.apache.syncope.common.lib.mod.AnyObjectMod;
 +import org.apache.syncope.common.lib.to.AnyObjectTO;
 +import org.apache.syncope.common.lib.to.MembershipTO;
 +import org.apache.syncope.common.lib.to.RelationshipTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.ClientExceptionType;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.core.misc.spring.BeanUtils;
 +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 +import org.apache.syncope.core.persistence.api.entity.AnyType;
 +import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership;
 +import org.apache.syncope.core.persistence.api.entity.anyobject.ARelationship;
 +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 +import org.apache.syncope.core.persistence.api.entity.group.Group;
 +import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.stereotype.Component;
 +import org.springframework.transaction.annotation.Transactional;
 +
 +@Component
 +@Transactional(rollbackFor = { Throwable.class })
 +public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements AnyObjectDataBinder {
 +
 +    private static final String[] IGNORE_PROPERTIES = {
 +        "type", "realm", "auxClasses", "relationships", "memberships", "dynGroups",
 +        "plainAttrs", "derAttrs", "virAttrs", "resources"
 +    };
 +
 +    @Autowired
 +    private AnyTypeDAO anyTypeDAO;
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public AnyObjectTO getAnyObjectTO(final Long key) {
-         return getAnyObjectTO(anyObjectDAO.authFind(key));
++        return getAnyObjectTO(anyObjectDAO.authFind(key), true);
 +    }
 +
 +    @Override
-     public AnyObjectTO getAnyObjectTO(final AnyObject anyObject) {
++    public AnyObjectTO getAnyObjectTO(final AnyObject anyObject, final boolean details) {
 +        AnyObjectTO anyObjectTO = new AnyObjectTO();
 +        anyObjectTO.setType(anyObject.getType().getKey());
 +
 +        BeanUtils.copyProperties(anyObject, anyObjectTO, IGNORE_PROPERTIES);
 +
-         virAttrHander.retrieveVirAttrValues(anyObject);
++        if (details) {
++            virAttrHander.retrieveVirAttrValues(anyObject);
++        }
++
 +        fillTO(anyObjectTO, anyObject.getRealm().getFullPath(), anyObject.getAuxClasses(),
 +                anyObject.getPlainAttrs(), anyObject.getDerAttrs(), anyObject.getVirAttrs(),
 +                anyObjectDAO.findAllResources(anyObject));
 +
-         // relationships
-         CollectionUtils.collect(anyObject.getRelationships(), new Transformer<ARelationship, RelationshipTO>() {
++        if (details) {
++            // relationships
++            CollectionUtils.collect(anyObject.getRelationships(), new Transformer<ARelationship, RelationshipTO>() {
 +
-             @Override
-             public RelationshipTO transform(final ARelationship relationship) {
-                 return AnyObjectDataBinderImpl.this.getRelationshipTO(relationship);
-             }
++                @Override
++                public RelationshipTO transform(final ARelationship relationship) {
++                    return AnyObjectDataBinderImpl.this.getRelationshipTO(relationship);
++                }
 +
-         }, anyObjectTO.getRelationships());
++            }, anyObjectTO.getRelationships());
 +
-         // memberships
-         CollectionUtils.collect(anyObject.getMemberships(), new Transformer<AMembership, MembershipTO>() {
++            // memberships
++            CollectionUtils.collect(anyObject.getMemberships(), new Transformer<AMembership, MembershipTO>() {
 +
-             @Override
-             public MembershipTO transform(final AMembership membership) {
-                 return AnyObjectDataBinderImpl.this.getMembershipTO(membership);
-             }
-         }, anyObjectTO.getMemberships());
++                @Override
++                public MembershipTO transform(final AMembership membership) {
++                    return AnyObjectDataBinderImpl.this.getMembershipTO(membership);
++                }
++            }, anyObjectTO.getMemberships());
 +
-         // dynamic memberships
-         CollectionUtils.collect(anyObjectDAO.findDynGroupMemberships(anyObject), new Transformer<Group, Long>() {
++            // dynamic memberships
++            CollectionUtils.collect(anyObjectDAO.findDynGroupMemberships(anyObject), new Transformer<Group, Long>() {
 +
-             @Override
-             public Long transform(final Group group) {
-                 return group.getKey();
-             }
-         }, anyObjectTO.getDynGroups());
++                @Override
++                public Long transform(final Group group) {
++                    return group.getKey();
++                }
++            }, anyObjectTO.getDynGroups());
++        }
 +
 +        return anyObjectTO;
 +    }
 +
 +    @Override
 +    public void create(final AnyObject anyObject, final AnyObjectTO anyObjectTO) {
 +        AnyType type = anyTypeDAO.find(anyObjectTO.getType());
 +        if (type == null) {
 +            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidAnyType);
 +            sce.getElements().add(anyObjectTO.getType());
 +            throw sce;
 +        }
 +        anyObject.setType(type);
 +
 +        SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
 +
 +        // relationships
 +        for (RelationshipTO relationshipTO : anyObjectTO.getRelationships()) {
 +            AnyObject otherEnd = anyObjectDAO.find(relationshipTO.getRightKey());
 +
 +            if (otherEnd == null) {
 +                if (LOG.isDebugEnabled()) {
 +                    LOG.debug("Ignoring invalid anyObject " + relationshipTO.getRightKey());
 +                }
 +            } else {
 +                ARelationship relationship = null;
 +                if (anyObject.getKey() != null) {
 +                    relationship = anyObject.getRelationship(otherEnd.getKey());
 +                }
 +                if (relationship == null) {
 +                    relationship = entityFactory.newEntity(ARelationship.class);
 +                    relationship.setRightEnd(otherEnd);
 +                    relationship.setLeftEnd(anyObject);
 +
 +                    anyObject.add(relationship);
 +                }
 +            }
 +        }
 +
 +        // memberships
 +        for (MembershipTO membershipTO : anyObjectTO.getMemberships()) {
 +            Group group = groupDAO.find(membershipTO.getRightKey());
 +
 +            if (group == null) {
 +                if (LOG.isDebugEnabled()) {
 +                    LOG.debug("Ignoring invalid group " + membershipTO.getGroupName());
 +                }
 +            } else {
 +                AMembership membership = null;
 +                if (anyObject.getKey() != null) {
 +                    membership = anyObject.getMembership(group.getKey());
 +                }
 +                if (membership == null) {
 +                    membership = entityFactory.newEntity(AMembership.class);
 +                    membership.setRightEnd(group);
 +                    membership.setLeftEnd(anyObject);
 +
 +                    anyObject.add(membership);
 +                }
 +            }
 +        }
 +
 +        // realm, attributes, derived attributes, virtual attributes and resources
 +        fill(anyObject, anyObjectTO, anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT), scce);
 +    }
 +
 +    @Override
 +    public PropagationByResource update(final AnyObject toBeUpdated, final AnyObjectMod anyObjectMod) {
 +        // Re-merge any pending change from workflow tasks
 +        final AnyObject anyObject = anyObjectDAO.save(toBeUpdated);
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +
 +        SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
 +
 +        Collection<String> currentResources = anyObjectDAO.findAllResourceNames(anyObject);
 +
 +        // fetch connObjectKeys before update
 +        Map<String, String> oldConnObjectKeys = getConnObjectKeys(anyObject);
 +
 +        // attributes, derived attributes, virtual attributes and resources
 +        propByRes.merge(fill(anyObject, anyObjectMod, anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT), scce));
 +
 +        Set<String> toBeDeprovisioned = new HashSet<>();
 +        Set<String> toBeProvisioned = new HashSet<>();
 +
 +        // relationships to be removed
 +        for (Long anyObjectKey : anyObjectMod.getRelationshipsToRemove()) {
 +            LOG.debug("Relationship to be removed for any object {}", anyObjectKey);
 +
 +            ARelationship relationship = anyObject.getRelationship(anyObjectKey);
 +            if (relationship == null) {
 +                LOG.warn("Invalid anyObject key specified for relationship to be removed: {}", anyObjectKey);
 +            } else {
 +                if (!anyObjectMod.getRelationshipsToAdd().contains(anyObjectKey)) {
 +                    anyObject.remove(relationship);
 +                    toBeDeprovisioned.addAll(relationship.getRightEnd().getResourceNames());
 +                }
 +            }
 +        }
 +
 +        // relationships to be added
 +        for (Long anyObjectKey : anyObjectMod.getRelationshipsToAdd()) {
 +            LOG.debug("Relationship to be added for any object {}", anyObjectKey);
 +
 +            AnyObject otherEnd = anyObjectDAO.find(anyObjectKey);
 +            if (otherEnd == null) {
 +                LOG.debug("Ignoring invalid any object {}", anyObjectKey);
 +            } else {
 +                ARelationship relationship = anyObject.getRelationship(otherEnd.getKey());
 +                if (relationship == null) {
 +                    relationship = entityFactory.newEntity(ARelationship.class);
 +                    relationship.setRightEnd(otherEnd);
 +                    relationship.setLeftEnd(anyObject);
 +
 +                    anyObject.add(relationship);
 +
 +                    toBeProvisioned.addAll(otherEnd.getResourceNames());
 +                }
 +            }
 +        }
 +
 +        // memberships to be removed
 +        for (Long groupKey : anyObjectMod.getMembershipsToRemove()) {
 +            LOG.debug("Membership to be removed for group {}", groupKey);
 +
 +            AMembership membership = anyObject.getMembership(groupKey);
 +            if (membership == null) {
 +                LOG.warn("Invalid group key specified for membership to be removed: {}", groupKey);
 +            } else {
 +                if (!anyObjectMod.getMembershipsToAdd().contains(groupKey)) {
 +                    anyObject.remove(membership);
 +                    toBeDeprovisioned.addAll(membership.getRightEnd().getResourceNames());
 +                }
 +            }
 +        }
 +
 +        // memberships to be added
 +        for (Long groupKey : anyObjectMod.getMembershipsToAdd()) {
 +            LOG.debug("Membership to be added for group {}", groupKey);
 +
 +            Group group = groupDAO.find(groupKey);
 +            if (group == null) {
 +                LOG.debug("Ignoring invalid group {}", groupKey);
 +            } else {
 +                AMembership membership = anyObject.getMembership(group.getKey());
 +                if (membership == null) {
 +                    membership = entityFactory.newEntity(AMembership.class);
 +                    membership.setRightEnd(group);
 +                    membership.setLeftEnd(anyObject);
 +
 +                    anyObject.add(membership);
 +
 +                    toBeProvisioned.addAll(group.getResourceNames());
 +                }
 +            }
 +        }
 +
 +        propByRes.addAll(ResourceOperation.DELETE, toBeDeprovisioned);
 +        propByRes.addAll(ResourceOperation.UPDATE, toBeProvisioned);
 +
 +        /**
 +         * In case of new memberships all the current resources have to be updated in order to propagate new group and
 +         * membership attribute values.
 +         */
 +        if (!toBeDeprovisioned.isEmpty() || !toBeProvisioned.isEmpty()) {
 +            currentResources.removeAll(toBeDeprovisioned);
 +            propByRes.addAll(ResourceOperation.UPDATE, currentResources);
 +        }
 +
 +        // check if some connObjectKey was changed by the update above
 +        Map<String, String> newcCnnObjectKeys = getConnObjectKeys(anyObject);
 +        for (Map.Entry<String, String> entry : oldConnObjectKeys.entrySet()) {
 +            if (newcCnnObjectKeys.containsKey(entry.getKey())
 +                    && !entry.getValue().equals(newcCnnObjectKeys.get(entry.getKey()))) {
 +
 +                propByRes.addOldConnObjectKey(entry.getKey(), entry.getValue());
 +                propByRes.add(ResourceOperation.UPDATE, entry.getKey());
 +            }
 +        }
 +
 +        return propByRes;
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
index a393fda,0000000..b40cc88
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
@@@ -1,229 -1,0 +1,231 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.provisioning.java.data;
 +
 +import java.util.Map;
 +import org.apache.syncope.common.lib.SyncopeClientCompositeException;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +import org.apache.syncope.common.lib.mod.GroupMod;
 +import org.apache.syncope.common.lib.to.GroupTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.ClientExceptionType;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.core.persistence.api.entity.group.Group;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
 +import org.apache.syncope.core.misc.search.SearchCondConverter;
 +import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 +import org.apache.syncope.core.persistence.api.entity.DynGroupMembership;
 +import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership;
 +import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership;
 +import org.springframework.stereotype.Component;
 +import org.springframework.transaction.annotation.Transactional;
 +
 +@Component
 +@Transactional(rollbackFor = { Throwable.class })
 +public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupDataBinder {
 +
 +    private void setDynMembership(final Group group, final AnyTypeKind anyTypeKind, final String dynMembershipFIQL) {
 +        SearchCond dynMembershipCond = SearchCondConverter.convert(dynMembershipFIQL);
 +        if (!dynMembershipCond.isValid()) {
 +            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchExpression);
 +            sce.getElements().add(dynMembershipFIQL);
 +            throw sce;
 +        }
 +
 +        DynGroupMembership<?> dynMembership;
 +        if (anyTypeKind == AnyTypeKind.ANY_OBJECT && group.getADynMembership() == null) {
 +            dynMembership = entityFactory.newEntity(ADynGroupMembership.class);
 +            dynMembership.setGroup(group);
 +            group.setADynMembership((ADynGroupMembership) dynMembership);
 +        } else if (anyTypeKind == AnyTypeKind.USER && group.getUDynMembership() == null) {
 +            dynMembership = entityFactory.newEntity(UDynGroupMembership.class);
 +            dynMembership.setGroup(group);
 +            group.setUDynMembership((UDynGroupMembership) dynMembership);
 +        } else {
 +            dynMembership = anyTypeKind == AnyTypeKind.ANY_OBJECT
 +                    ? group.getADynMembership()
 +                    : group.getUDynMembership();
 +        }
 +        dynMembership.setFIQLCond(dynMembershipFIQL);
 +    }
 +
 +    @Override
 +    public Group create(final Group group, final GroupTO groupTO) {
 +        SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
 +
 +        // name
 +        SyncopeClientException invalidGroups = SyncopeClientException.build(ClientExceptionType.InvalidGroup);
 +        if (groupTO.getName() == null) {
 +            LOG.error("No name specified for this group");
 +
 +            invalidGroups.getElements().add("No name specified for this group");
 +        } else {
 +            group.setName(groupTO.getName());
 +        }
 +
 +        // attributes, derived attributes, virtual attributes and resources
 +        fill(group, groupTO, anyUtilsFactory.getInstance(AnyTypeKind.GROUP), scce);
 +
 +        // owner
 +        if (groupTO.getUserOwner() != null) {
 +            User owner = userDAO.find(groupTO.getUserOwner());
 +            if (owner == null) {
 +                LOG.warn("Ignoring invalid user specified as owner: {}", groupTO.getUserOwner());
 +            } else {
 +                group.setUserOwner(owner);
 +            }
 +        }
 +        if (groupTO.getGroupOwner() != null) {
 +            Group owner = groupDAO.find(groupTO.getGroupOwner());
 +            if (owner == null) {
 +                LOG.warn("Ignoring invalid group specified as owner: {}", groupTO.getGroupOwner());
 +            } else {
 +                group.setGroupOwner(owner);
 +            }
 +        }
 +
 +        if (groupTO.getADynMembershipCond() != null) {
 +            setDynMembership(group, AnyTypeKind.ANY_OBJECT, groupTO.getADynMembershipCond());
 +        }
 +        if (groupTO.getUDynMembershipCond() != null) {
 +            setDynMembership(group, AnyTypeKind.USER, groupTO.getUDynMembershipCond());
 +        }
 +
 +        return group;
 +    }
 +
 +    @Override
 +    public PropagationByResource update(final Group toBeUpdated, final GroupMod groupMod) {
 +        // Re-merge any pending change from workflow tasks
 +        Group group = groupDAO.save(toBeUpdated);
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +
 +        SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
 +
 +        // fetch connObjectKeys before update
 +        Map<String, String> oldConnObjectKeys = getConnObjectKeys(group);
 +
 +        // realm
 +        setRealm(group, groupMod);
 +        // name
 +        if (groupMod.getName() != null && !groupMod.getName().equals(group.getName())) {
 +            propByRes.addAll(ResourceOperation.UPDATE, group.getResourceNames());
 +
 +            group.setName(groupMod.getName());
 +        }
 +
 +        // owner
 +        if (groupMod.getUserOwner() != null) {
 +            group.setUserOwner(groupMod.getUserOwner().getKey() == null
 +                    ? null
 +                    : userDAO.find(groupMod.getUserOwner().getKey()));
 +        }
 +        if (groupMod.getGroupOwner() != null) {
 +            group.setGroupOwner(groupMod.getGroupOwner().getKey() == null
 +                    ? null
 +                    : groupDAO.find(groupMod.getGroupOwner().getKey()));
 +        }
 +
 +        // attributes, derived attributes, virtual attributes and resources
 +        propByRes.merge(fill(group, groupMod, anyUtilsFactory.getInstance(AnyTypeKind.GROUP), scce));
 +
 +        // check if some connObjectKey was changed by the update above
 +        Map<String, String> newConnObjectKeys = getConnObjectKeys(group);
 +        for (Map.Entry<String, String> entry : oldConnObjectKeys.entrySet()) {
 +            if (newConnObjectKeys.containsKey(entry.getKey())
 +                    && !entry.getValue().equals(newConnObjectKeys.get(entry.getKey()))) {
 +
 +                propByRes.addOldConnObjectKey(entry.getKey(), entry.getValue());
 +                propByRes.add(ResourceOperation.UPDATE, entry.getKey());
 +            }
 +        }
 +
 +        // dynamic membership
 +        if (group.getADynMembership() != null && groupMod.getADynMembershipCond() == null) {
 +            group.setADynMembership(null);
 +        } else if (group.getADynMembership() == null && groupMod.getADynMembershipCond() != null) {
 +            setDynMembership(group, AnyTypeKind.ANY_OBJECT, groupMod.getADynMembershipCond());
 +        } else if (group.getADynMembership() != null && groupMod.getADynMembershipCond() != null
 +                && !group.getADynMembership().getFIQLCond().equals(groupMod.getADynMembershipCond())) {
 +
 +            group.getADynMembership().getMembers().clear();
 +            setDynMembership(group, AnyTypeKind.ANY_OBJECT, groupMod.getADynMembershipCond());
 +        }
 +        if (group.getUDynMembership() != null && groupMod.getUDynMembershipCond() == null) {
 +            group.setUDynMembership(null);
 +        } else if (group.getUDynMembership() == null && groupMod.getUDynMembershipCond() != null) {
 +            setDynMembership(group, AnyTypeKind.USER, groupMod.getUDynMembershipCond());
 +        } else if (group.getUDynMembership() != null && groupMod.getUDynMembershipCond() != null
 +                && !group.getUDynMembership().getFIQLCond().equals(groupMod.getUDynMembershipCond())) {
 +
 +            group.getUDynMembership().getMembers().clear();
 +            setDynMembership(group, AnyTypeKind.USER, groupMod.getUDynMembershipCond());
 +        }
 +
 +        return propByRes;
 +    }
 +
 +    @SuppressWarnings("unchecked")
 +    @Transactional(readOnly = true)
 +    @Override
-     public GroupTO getGroupTO(final Group group) {
-         virAttrHander.retrieveVirAttrValues(group);
- 
++    public GroupTO getGroupTO(final Group group, final boolean details) {
 +        GroupTO groupTO = new GroupTO();
 +
 +        // set sys info
 +        groupTO.setCreator(group.getCreator());
 +        groupTO.setCreationDate(group.getCreationDate());
 +        groupTO.setLastModifier(group.getLastModifier());
 +        groupTO.setLastChangeDate(group.getLastChangeDate());
 +
 +        groupTO.setKey(group.getKey());
 +        groupTO.setName(group.getName());
 +
 +        if (group.getUserOwner() != null) {
 +            groupTO.setUserOwner(group.getUserOwner().getKey());
 +        }
 +        if (group.getGroupOwner() != null) {
 +            groupTO.setGroupOwner(group.getGroupOwner().getKey());
 +        }
 +
++        if (details) {
++            virAttrHander.retrieveVirAttrValues(group);
++        }
++
 +        fillTO(groupTO, group.getRealm().getFullPath(), group.getAuxClasses(),
 +                group.getPlainAttrs(), group.getDerAttrs(), group.getVirAttrs(), group.getResources());
 +
 +        if (group.getADynMembership() != null) {
 +            groupTO.setADynMembershipCond(group.getADynMembership().getFIQLCond());
 +        }
 +        if (group.getUDynMembership() != null) {
 +            groupTO.setUDynMembershipCond(group.getUDynMembership().getFIQLCond());
 +        }
 +
 +        return groupTO;
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public GroupTO getGroupTO(final Long key) {
-         return getGroupTO(groupDAO.authFind(key));
++        return getGroupTO(groupDAO.authFind(key), true);
 +    }
 +}


[6/7] syncope git commit: [SYNCOPE-676] Merge from 1_2_X

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
----------------------------------------------------------------------
diff --cc core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
index 257bf42,0000000..05db8b5
mode 100644,000000..100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
@@@ -1,342 -1,0 +1,344 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.logic;
 +
 +import java.lang.reflect.Method;
 +import java.util.ArrayList;
 +import java.util.Collection;
 +import java.util.Collections;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +import javax.annotation.Resource;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Transformer;
 +import org.apache.commons.lang3.ArrayUtils;
 +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.mod.AnyObjectMod;
 +import org.apache.syncope.common.lib.to.PropagationStatus;
 +import org.apache.syncope.common.lib.to.AnyObjectTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.ClientExceptionType;
 +import org.apache.syncope.common.lib.types.Entitlement;
 +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 +import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 +import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 +import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
 +import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 +import org.apache.syncope.core.misc.security.AuthContextUtils;
 +import org.apache.syncope.core.misc.security.UnauthorizedException;
 +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 +import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 +import org.apache.syncope.core.provisioning.api.AnyTransformer;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.security.access.prepost.PreAuthorize;
 +import org.springframework.stereotype.Component;
 +import org.springframework.transaction.annotation.Transactional;
 +import org.springframework.transaction.interceptor.TransactionInterceptor;
 +
 +/**
 + * Note that this controller does not extend {@link AbstractTransactionalLogic}, hence does not provide any
 + * Spring's Transactional logic at class level.
 + */
 +@Component
 +public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectMod> {
 +
 +    @Autowired
 +    protected AnyObjectDAO anyObjectDAO;
 +
 +    @Autowired
 +    protected AnySearchDAO searchDAO;
 +
 +    @Autowired
 +    protected AnyObjectDataBinder binder;
 +
 +    @Autowired
 +    protected PropagationManager propagationManager;
 +
 +    @Autowired
 +    protected PropagationTaskExecutor taskExecutor;
 +
 +    @Autowired
 +    protected AnyTransformer attrTransformer;
 +
 +    @Resource(name = "anonymousUser")
 +    protected String anonymousUser;
 +
 +    @Autowired
 +    protected AnyObjectProvisioningManager provisioningManager;
 +
 +    @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_READ + "')")
 +    @Transactional(readOnly = true)
 +    @Override
 +    public AnyObjectTO read(final Long anyObjectKey) {
 +        return binder.getAnyObjectTO(anyObjectKey);
 +    }
 +
 +    @PreAuthorize("isAuthenticated()")
 +    @Transactional(readOnly = true, rollbackFor = { Throwable.class })
 +    @Override
 +    public int count(final List<String> realms) {
 +        return anyObjectDAO.count(getEffectiveRealms(SyncopeConstants.FULL_ADMIN_REALMS, realms));
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_LIST + "')")
 +    @Transactional(readOnly = true)
 +    @Override
 +    public List<AnyObjectTO> list(
-             final int page, final int size, final List<OrderByClause> orderBy, final List<String> realms) {
++            final int page, final int size, final List<OrderByClause> orderBy,
++            final List<String> realms, final boolean details) {
 +
-         return list(null, page, size, orderBy, realms);
++        return list(null, page, size, orderBy, realms, details);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_LIST + "')")
 +    @Transactional(readOnly = true)
 +    public List<AnyObjectTO> list(final String type,
-             final int page, final int size, final List<OrderByClause> orderBy, final List<String> realms) {
++            final int page, final int size, final List<OrderByClause> orderBy,
++            final List<String> realms, final boolean details) {
 +
 +        Set<String> effectiveRealms = getEffectiveRealms(SyncopeConstants.FULL_ADMIN_REALMS, realms);
 +
 +        return CollectionUtils.collect(StringUtils.isBlank(type)
 +                ? anyObjectDAO.findAll(effectiveRealms, page, size, orderBy)
 +                : anyObjectDAO.findAll(type, effectiveRealms, page, size, orderBy),
 +                new Transformer<AnyObject, AnyObjectTO>() {
 +
 +                    @Override
 +                    public AnyObjectTO transform(final AnyObject input) {
-                         return binder.getAnyObjectTO(input);
++                        return binder.getAnyObjectTO(input, details);
 +                    }
 +                }, new ArrayList<AnyObjectTO>());
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_SEARCH + "')")
 +    @Transactional(readOnly = true, rollbackFor = { Throwable.class })
 +    @Override
 +    public int searchCount(final SearchCond searchCondition, final List<String> realms) {
 +        return searchDAO.count(
 +                getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_SEARCH), realms),
 +                searchCondition, AnyTypeKind.ANY_OBJECT);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_SEARCH + "')")
 +    @Transactional(readOnly = true, rollbackFor = { Throwable.class })
 +    @Override
 +    public List<AnyObjectTO> search(final SearchCond searchCondition, final int page, final int size,
-             final List<OrderByClause> orderBy, final List<String> realms) {
++            final List<OrderByClause> orderBy, final List<String> realms, final boolean details) {
 +
 +        List<AnyObject> matchingAnyObjects = searchDAO.search(
 +                getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_SEARCH), realms),
 +                searchCondition, page, size, orderBy, AnyTypeKind.ANY_OBJECT);
 +        return CollectionUtils.collect(matchingAnyObjects, new Transformer<AnyObject, AnyObjectTO>() {
 +
 +            @Override
 +            public AnyObjectTO transform(final AnyObject input) {
-                 return binder.getAnyObjectTO(input);
++                return binder.getAnyObjectTO(input, details);
 +            }
 +        }, new ArrayList<AnyObjectTO>());
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_CREATE + "')")
 +    @Override
 +    public AnyObjectTO create(final AnyObjectTO anyObjectTO) {
 +        if (anyObjectTO.getRealm() == null) {
 +            throw SyncopeClientException.build(ClientExceptionType.InvalidRealm);
 +        }
 +        Set<String> effectiveRealms = getEffectiveRealms(
 +                AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_CREATE),
 +                Collections.singleton(anyObjectTO.getRealm()));
 +        if (effectiveRealms.isEmpty()) {
 +            throw new UnauthorizedException(AnyTypeKind.ANY_OBJECT, null);
 +        }
 +
 +        // Any transformation (if configured)
 +        AnyObjectTO actual = attrTransformer.transform(anyObjectTO);
 +        LOG.debug("Transformed: {}", actual);
 +
 +        if (anyObjectTO.getType() == null) {
 +            throw SyncopeClientException.build(ClientExceptionType.InvalidAnyType);
 +        }
 +
 +        /*
 +         * Actual operations: workflow, propagation
 +         */
 +        Map.Entry<Long, List<PropagationStatus>> created = provisioningManager.create(anyObjectTO);
 +        AnyObjectTO savedTO = binder.getAnyObjectTO(created.getKey());
 +        savedTO.getPropagationStatusTOs().addAll(created.getValue());
 +        return savedTO;
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')")
 +    @Override
 +    public AnyObjectTO update(final AnyObjectMod anyObjectMod) {
 +        AnyObject anyObject = anyObjectDAO.authFind(anyObjectMod.getKey());
 +        if (anyObject == null) {
 +            throw new NotFoundException("AnyObject with key " + anyObjectMod.getKey());
 +        }
 +        Set<String> effectiveRealms = getEffectiveRealms(
 +                AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_UPDATE),
 +                Collections.singleton(anyObject.getRealm().getFullPath()));
 +        if (effectiveRealms.isEmpty()) {
 +            throw new UnauthorizedException(AnyTypeKind.ANY_OBJECT, anyObject.getKey());
 +        }
 +
 +        // Any transformation (if configured)
 +        AnyObjectMod actual = attrTransformer.transform(anyObjectMod);
 +        LOG.debug("Transformed: {}", actual);
 +
 +        Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(anyObjectMod);
 +
 +        AnyObjectTO updatedTO = binder.getAnyObjectTO(updated.getKey());
 +        updatedTO.getPropagationStatusTOs().addAll(updated.getValue());
 +        return updatedTO;
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_DELETE + "')")
 +    @Override
 +    public AnyObjectTO delete(final Long anyObjectKey) {
 +        AnyObject anyObject = anyObjectDAO.authFind(anyObjectKey);
 +        if (anyObject == null) {
 +            throw new NotFoundException("AnyObject with key " + anyObjectKey);
 +        }
 +        Set<String> effectiveRealms = getEffectiveRealms(
 +                AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_UPDATE),
 +                Collections.singleton(anyObject.getRealm().getFullPath()));
 +        if (effectiveRealms.isEmpty()) {
 +            throw new UnauthorizedException(AnyTypeKind.ANY_OBJECT, anyObject.getKey());
 +        }
 +
 +        List<PropagationStatus> statuses = provisioningManager.delete(anyObjectKey);
 +
 +        AnyObjectTO anyObjectTO = new AnyObjectTO();
 +        anyObjectTO.setKey(anyObjectKey);
 +
 +        anyObjectTO.getPropagationStatusTOs().addAll(statuses);
 +
 +        return anyObjectTO;
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    @Override
 +    public AnyObjectTO unlink(final Long anyObjectKey, final Collection<String> resources) {
 +        AnyObjectMod anyObjectMod = new AnyObjectMod();
 +        anyObjectMod.setKey(anyObjectKey);
 +        anyObjectMod.getResourcesToRemove().addAll(resources);
 +        final Long updatedResult = provisioningManager.unlink(anyObjectMod);
 +
 +        return binder.getAnyObjectTO(updatedResult);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    @Override
 +    public AnyObjectTO link(final Long anyObjectKey, final Collection<String> resources) {
 +        AnyObjectMod anyObjectMod = new AnyObjectMod();
 +        anyObjectMod.setKey(anyObjectKey);
 +        anyObjectMod.getResourcesToAdd().addAll(resources);
 +        return binder.getAnyObjectTO(provisioningManager.link(anyObjectMod));
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    @Override
 +    public AnyObjectTO unassign(final Long anyObjectKey, final Collection<String> resources) {
 +        AnyObjectMod anyObjectMod = new AnyObjectMod();
 +        anyObjectMod.setKey(anyObjectKey);
 +        anyObjectMod.getResourcesToRemove().addAll(resources);
 +        return update(anyObjectMod);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    @Override
 +    public AnyObjectTO assign(final Long anyObjectKey, final Collection<String> resources,
 +            final boolean changePwd, final String password) {
 +
 +        AnyObjectMod userMod = new AnyObjectMod();
 +        userMod.setKey(anyObjectKey);
 +        userMod.getResourcesToAdd().addAll(resources);
 +        return update(userMod);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    @Override
 +    public AnyObjectTO deprovision(final Long anyObjectKey, final Collection<String> resources) {
 +        AnyObject anyObject = anyObjectDAO.authFind(anyObjectKey);
 +
 +        List<PropagationStatus> statuses = provisioningManager.deprovision(anyObjectKey, resources);
 +
-         AnyObjectTO updatedTO = binder.getAnyObjectTO(anyObject);
++        AnyObjectTO updatedTO = binder.getAnyObjectTO(anyObject, true);
 +        updatedTO.getPropagationStatusTOs().addAll(statuses);
 +        return updatedTO;
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    @Override
 +    public AnyObjectTO provision(final Long anyObjectKey, final Collection<String> resources,
 +            final boolean changePwd, final String password) {
 +
 +        AnyObjectTO original = binder.getAnyObjectTO(anyObjectKey);
 +
 +        //trick: assign and retrieve propagation statuses ...
 +        original.getPropagationStatusTOs().addAll(
 +                assign(anyObjectKey, resources, changePwd, password).getPropagationStatusTOs());
 +
 +        // .... rollback.
 +        TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
 +        return original;
 +    }
 +
 +    @Override
 +    protected AnyObjectTO resolveReference(final Method method, final Object... args)
 +            throws UnresolvedReferenceException {
 +
 +        Long key = null;
 +
 +        if (ArrayUtils.isNotEmpty(args)) {
 +            for (int i = 0; key == null && i < args.length; i++) {
 +                if (args[i] instanceof Long) {
 +                    key = (Long) args[i];
 +                } else if (args[i] instanceof AnyObjectTO) {
 +                    key = ((AnyObjectTO) args[i]).getKey();
 +                } else if (args[i] instanceof AnyObjectMod) {
 +                    key = ((AnyObjectMod) args[i]).getKey();
 +                }
 +            }
 +        }
 +
 +        if ((key != null) && !key.equals(0L)) {
 +            try {
 +                return binder.getAnyObjectTO(key);
 +            } catch (Throwable ignore) {
 +                LOG.debug("Unresolved reference", ignore);
 +                throw new UnresolvedReferenceException(ignore);
 +            }
 +        }
 +
 +        throw new UnresolvedReferenceException();
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
----------------------------------------------------------------------
diff --cc core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
index 877a272,0000000..ff5cd64
mode 100644,000000..100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
@@@ -1,357 -1,0 +1,358 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.logic;
 +
 +import java.lang.reflect.Method;
 +import java.util.ArrayList;
 +import java.util.Collection;
 +import java.util.Collections;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +import javax.annotation.Resource;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Transformer;
 +import org.apache.commons.lang3.ArrayUtils;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +import org.apache.syncope.common.lib.SyncopeConstants;
 +import org.apache.syncope.common.lib.mod.GroupMod;
 +import org.apache.syncope.common.lib.to.PropagationStatus;
 +import org.apache.syncope.common.lib.to.GroupTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.ClientExceptionType;
 +import org.apache.syncope.common.lib.types.Entitlement;
 +import org.apache.syncope.core.misc.RealmUtils;
 +import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 +import org.apache.syncope.core.persistence.api.dao.UserDAO;
 +import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 +import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 +import org.apache.syncope.core.persistence.api.entity.group.Group;
 +import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
 +import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 +import org.apache.syncope.core.misc.security.AuthContextUtils;
 +import org.apache.syncope.core.misc.security.UnauthorizedException;
 +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 +import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 +import org.apache.syncope.core.provisioning.api.AnyTransformer;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.security.access.prepost.PreAuthorize;
 +import org.springframework.stereotype.Component;
 +import org.springframework.transaction.annotation.Transactional;
 +import org.springframework.transaction.interceptor.TransactionInterceptor;
 +
 +/**
 + * Note that this controller does not extend {@link AbstractTransactionalLogic}, hence does not provide any
 + * Spring's Transactional logic at class level.
 + */
 +@Component
 +public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupMod> {
 +
 +    @Autowired
 +    protected GroupDAO groupDAO;
 +
 +    @Autowired
 +    protected UserDAO userDAO;
 +
 +    @Autowired
 +    protected AnySearchDAO searchDAO;
 +
 +    @Autowired
 +    protected GroupDataBinder binder;
 +
 +    @Autowired
 +    protected PropagationManager propagationManager;
 +
 +    @Autowired
 +    protected PropagationTaskExecutor taskExecutor;
 +
 +    @Autowired
 +    protected AnyTransformer attrTransformer;
 +
 +    @Resource(name = "anonymousUser")
 +    protected String anonymousUser;
 +
 +    @Autowired
 +    protected GroupProvisioningManager provisioningManager;
 +
 +    @PreAuthorize("hasRole('" + Entitlement.GROUP_READ + "')")
 +    @Transactional(readOnly = true)
 +    @Override
 +    public GroupTO read(final Long groupKey) {
 +        return binder.getGroupTO(groupKey);
 +    }
 +
 +    @PreAuthorize("isAuthenticated() and not(hasRole('" + Entitlement.ANONYMOUS + "'))")
 +    @Transactional(readOnly = true)
 +    public List<GroupTO> own() {
 +        return CollectionUtils.collect(
 +                userDAO.findAllGroups(userDAO.find(AuthContextUtils.getAuthenticatedUsername())),
 +                new Transformer<Group, GroupTO>() {
 +
 +                    @Override
 +                    public GroupTO transform(final Group input) {
-                         return binder.getGroupTO(input);
++                        return binder.getGroupTO(input, true);
 +                    }
 +                }, new ArrayList<GroupTO>());
 +    }
 +
 +    @PreAuthorize("isAuthenticated()")
 +    @Transactional(readOnly = true, rollbackFor = { Throwable.class })
 +    @Override
 +    public int count(final List<String> realms) {
 +        return groupDAO.count(getEffectiveRealms(SyncopeConstants.FULL_ADMIN_REALMS, realms));
 +    }
 +
 +    @PreAuthorize("isAuthenticated()")
 +    @Transactional(readOnly = true)
 +    @Override
 +    public List<GroupTO> list(
-             final int page, final int size, final List<OrderByClause> orderBy, final List<String> realms) {
++            final int page, final int size, final List<OrderByClause> orderBy,
++            final List<String> realms, final boolean details) {
 +
 +        return CollectionUtils.collect(groupDAO.findAll(
 +                getEffectiveRealms(SyncopeConstants.FULL_ADMIN_REALMS, realms),
 +                page, size, orderBy),
 +                new Transformer<Group, GroupTO>() {
 +
 +                    @Override
 +                    public GroupTO transform(final Group input) {
-                         return binder.getGroupTO(input);
++                        return binder.getGroupTO(input, details);
 +                    }
 +                }, new ArrayList<GroupTO>());
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.GROUP_SEARCH + "')")
 +    @Transactional(readOnly = true, rollbackFor = { Throwable.class })
 +    @Override
 +    public int searchCount(final SearchCond searchCondition, final List<String> realms) {
 +        return searchDAO.count(
 +                getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_SEARCH), realms),
 +                searchCondition, AnyTypeKind.GROUP);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.GROUP_SEARCH + "')")
 +    @Transactional(readOnly = true, rollbackFor = { Throwable.class })
 +    @Override
 +    public List<GroupTO> search(final SearchCond searchCondition, final int page, final int size,
-             final List<OrderByClause> orderBy, final List<String> realms) {
++            final List<OrderByClause> orderBy, final List<String> realms, final boolean details) {
 +
 +        final List<Group> matchingGroups = searchDAO.search(
 +                getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_SEARCH), realms),
 +                searchCondition, page, size, orderBy, AnyTypeKind.GROUP);
 +        return CollectionUtils.collect(matchingGroups, new Transformer<Group, GroupTO>() {
 +
 +            @Override
 +            public GroupTO transform(final Group input) {
-                 return binder.getGroupTO(input);
++                return binder.getGroupTO(input, details);
 +            }
 +        }, new ArrayList<GroupTO>());
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.GROUP_CREATE + "')")
 +    @Override
 +    public GroupTO create(final GroupTO groupTO) {
 +        if (groupTO.getRealm() == null) {
 +            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm);
 +            throw sce;
 +        }
 +        Set<String> effectiveRealms = getEffectiveRealms(
 +                AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_CREATE),
 +                Collections.singleton(groupTO.getRealm()));
 +        if (effectiveRealms.isEmpty()) {
 +            throw new UnauthorizedException(AnyTypeKind.GROUP, null);
 +        }
 +
 +        // Any transformation (if configured)
 +        GroupTO actual = attrTransformer.transform(groupTO);
 +        LOG.debug("Transformed: {}", actual);
 +
 +        /*
 +         * Actual operations: workflow, propagation
 +         */
 +        Map.Entry<Long, List<PropagationStatus>> created = provisioningManager.create(groupTO);
 +        GroupTO savedTO = binder.getGroupTO(created.getKey());
 +        savedTO.getPropagationStatusTOs().addAll(created.getValue());
 +        return savedTO;
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')")
 +    @Override
 +    public GroupTO update(final GroupMod groupMod) {
 +        Group group = groupDAO.authFind(groupMod.getKey());
 +        if (group == null) {
 +            throw new NotFoundException("Group with key " + groupMod.getKey());
 +        }
 +        Set<String> effectiveRealms = getEffectiveRealms(
 +                AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_UPDATE),
 +                Collections.singleton(RealmUtils.getGroupOwnerRealm(group.getRealm().getFullPath(), group.getKey())));
 +        if (effectiveRealms.isEmpty()) {
 +            throw new UnauthorizedException(AnyTypeKind.GROUP, group.getKey());
 +        }
 +
 +        // Any transformation (if configured)
 +        GroupMod actual = attrTransformer.transform(groupMod);
 +        LOG.debug("Transformed: {}", actual);
 +
 +        Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(groupMod);
 +
 +        GroupTO updatedTO = binder.getGroupTO(updated.getKey());
 +        updatedTO.getPropagationStatusTOs().addAll(updated.getValue());
 +        return updatedTO;
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.GROUP_DELETE + "')")
 +    @Override
 +    public GroupTO delete(final Long groupKey) {
 +        Group group = groupDAO.authFind(groupKey);
 +        if (group == null) {
 +            throw new NotFoundException("Group with key " + groupKey);
 +        }
 +        Set<String> effectiveRealms = getEffectiveRealms(
 +                AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_DELETE),
 +                Collections.singleton(RealmUtils.getGroupOwnerRealm(group.getRealm().getFullPath(), group.getKey())));
 +        if (effectiveRealms.isEmpty()) {
 +            throw new UnauthorizedException(AnyTypeKind.GROUP, group.getKey());
 +        }
 +
 +        List<Group> ownedGroups = groupDAO.findOwnedByGroup(groupKey);
 +        if (!ownedGroups.isEmpty()) {
 +            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.GroupOwnership);
 +            sce.getElements().addAll(CollectionUtils.collect(ownedGroups, new Transformer<Group, String>() {
 +
 +                @Override
 +                public String transform(final Group group) {
 +                    return group.getKey() + " " + group.getName();
 +                }
 +            }, new ArrayList<String>()));
 +            throw sce;
 +        }
 +
 +        List<PropagationStatus> statuses = provisioningManager.delete(groupKey);
 +
 +        GroupTO groupTO = new GroupTO();
 +        groupTO.setKey(groupKey);
 +
 +        groupTO.getPropagationStatusTOs().addAll(statuses);
 +
 +        return groupTO;
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    @Override
 +    public GroupTO unlink(final Long groupKey, final Collection<String> resources) {
 +        final GroupMod groupMod = new GroupMod();
 +        groupMod.setKey(groupKey);
 +        groupMod.getResourcesToRemove().addAll(resources);
 +        final Long updatedResult = provisioningManager.unlink(groupMod);
 +
 +        return binder.getGroupTO(updatedResult);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    @Override
 +    public GroupTO link(final Long groupKey, final Collection<String> resources) {
 +        final GroupMod groupMod = new GroupMod();
 +        groupMod.setKey(groupKey);
 +        groupMod.getResourcesToAdd().addAll(resources);
 +        return binder.getGroupTO(provisioningManager.link(groupMod));
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    @Override
 +    public GroupTO unassign(final Long groupKey, final Collection<String> resources) {
 +        final GroupMod groupMod = new GroupMod();
 +        groupMod.setKey(groupKey);
 +        groupMod.getResourcesToRemove().addAll(resources);
 +        return update(groupMod);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    @Override
 +    public GroupTO assign(
 +            final Long groupKey, final Collection<String> resources, final boolean changePwd, final String password) {
 +
 +        final GroupMod userMod = new GroupMod();
 +        userMod.setKey(groupKey);
 +        userMod.getResourcesToAdd().addAll(resources);
 +        return update(userMod);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    @Override
 +    public GroupTO deprovision(final Long groupKey, final Collection<String> resources) {
 +        final Group group = groupDAO.authFind(groupKey);
 +
 +        List<PropagationStatus> statuses = provisioningManager.deprovision(groupKey, resources);
 +
-         GroupTO updatedTO = binder.getGroupTO(group);
++        GroupTO updatedTO = binder.getGroupTO(group, true);
 +        updatedTO.getPropagationStatusTOs().addAll(statuses);
 +        return updatedTO;
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    @Override
 +    public GroupTO provision(
 +            final Long groupKey, final Collection<String> resources, final boolean changePwd, final String password) {
 +        GroupTO original = binder.getGroupTO(groupKey);
 +
 +        //trick: assign and retrieve propagation statuses ...
 +        original.getPropagationStatusTOs().addAll(
 +                assign(groupKey, resources, changePwd, password).getPropagationStatusTOs());
 +
 +        // .... rollback.
 +        TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
 +        return original;
 +    }
 +
 +    @Override
 +    protected GroupTO resolveReference(final Method method, final Object... args) throws UnresolvedReferenceException {
 +        Long key = null;
 +
 +        if (ArrayUtils.isNotEmpty(args)) {
 +            for (int i = 0; key == null && i < args.length; i++) {
 +                if (args[i] instanceof Long) {
 +                    key = (Long) args[i];
 +                } else if (args[i] instanceof GroupTO) {
 +                    key = ((GroupTO) args[i]).getKey();
 +                } else if (args[i] instanceof GroupMod) {
 +                    key = ((GroupMod) args[i]).getKey();
 +                }
 +            }
 +        }
 +
 +        if ((key != null) && !key.equals(0L)) {
 +            try {
 +                return binder.getGroupTO(key);
 +            } catch (Throwable ignore) {
 +                LOG.debug("Unresolved reference", ignore);
 +                throw new UnresolvedReferenceException(ignore);
 +            }
 +        }
 +
 +        throw new UnresolvedReferenceException();
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
----------------------------------------------------------------------
diff --cc core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
index 28e5cab,0000000..3aec28c
mode 100644,000000..100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
@@@ -1,465 -1,0 +1,466 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.logic;
 +
 +import java.lang.reflect.Method;
 +import java.security.AccessControlException;
 +import java.util.ArrayList;
 +import java.util.Collection;
 +import java.util.Collections;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Transformer;
 +import org.apache.commons.lang3.ArrayUtils;
 +import org.apache.commons.lang3.tuple.ImmutablePair;
 +import org.apache.commons.lang3.tuple.Pair;
 +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.PropagationStatus;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.ClientExceptionType;
 +import org.apache.syncope.common.lib.types.Entitlement;
 +import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 +import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 +import org.apache.syncope.core.persistence.api.dao.UserDAO;
 +import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 +import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 +import org.apache.syncope.core.persistence.api.entity.group.Group;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
 +import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 +import org.apache.syncope.core.misc.security.AuthContextUtils;
 +import org.apache.syncope.core.misc.security.UnauthorizedException;
 +import org.apache.syncope.core.misc.serialization.POJOHelper;
 +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 +import org.apache.syncope.core.provisioning.api.AnyTransformer;
 +import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.security.access.prepost.PreAuthorize;
 +import org.springframework.stereotype.Component;
 +import org.springframework.transaction.annotation.Transactional;
 +import org.springframework.transaction.interceptor.TransactionInterceptor;
 +
 +/**
 + * Note that this controller does not extend {@link AbstractTransactionalLogic}, hence does not provide any
 + * Spring's Transactional logic at class level.
 + */
 +@Component
 +public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
 +
 +    @Autowired
 +    protected UserDAO userDAO;
 +
 +    @Autowired
 +    protected GroupDAO groupDAO;
 +
 +    @Autowired
 +    protected AnySearchDAO searchDAO;
 +
 +    @Autowired
 +    protected UserDataBinder binder;
 +
 +    @Autowired
 +    protected VirAttrHandler virtAttrHandler;
 +
 +    @Autowired
 +    protected PropagationManager propagationManager;
 +
 +    @Autowired
 +    protected PropagationTaskExecutor taskExecutor;
 +
 +    @Autowired
 +    protected AnyTransformer anyTransformer;
 +
 +    @Autowired
 +    protected UserProvisioningManager provisioningManager;
 +
 +    @Autowired
 +    protected SyncopeLogic syncopeLogic;
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_READ + "')")
 +    public String getUsername(final Long key) {
 +        return binder.getUserTO(key).getUsername();
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_READ + "')")
 +    public Long getKey(final String username) {
 +        return binder.getUserTO(username).getKey();
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_LIST + "')")
 +    @Transactional(readOnly = true, rollbackFor = { Throwable.class })
 +    @Override
 +    public int count(final List<String> realms) {
 +        return userDAO.count(
 +                getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.USER_LIST), realms));
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_LIST + "')")
 +    @Transactional(readOnly = true, rollbackFor = { Throwable.class })
 +    @Override
 +    public List<UserTO> list(
-             final int page, final int size, final List<OrderByClause> orderBy, final List<String> realms) {
++            final int page, final int size, final List<OrderByClause> orderBy,
++            final List<String> realms, final boolean details) {
 +
 +        return CollectionUtils.collect(userDAO.findAll(
 +                getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.USER_LIST), realms),
 +                page, size, orderBy),
 +                new Transformer<User, UserTO>() {
 +
 +                    @Override
 +                    public UserTO transform(final User input) {
-                         return binder.getUserTO(input);
++                        return binder.getUserTO(input, details);
 +                    }
 +                }, new ArrayList<UserTO>());
 +    }
 +
 +    @PreAuthorize("isAuthenticated()")
 +    @Transactional(readOnly = true)
 +    public Pair<String, UserTO> readSelf() {
 +        return ImmutablePair.of(
 +                POJOHelper.serialize(AuthContextUtils.getAuthorizations()),
 +                binder.getAuthenticatedUserTO());
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_READ + "')")
 +    @Transactional(readOnly = true)
 +    @Override
 +    public UserTO read(final Long key) {
 +        return binder.getUserTO(key);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_SEARCH + "')")
 +    @Transactional(readOnly = true, rollbackFor = { Throwable.class })
 +    @Override
 +    public int searchCount(final SearchCond searchCondition, final List<String> realms) {
 +        return searchDAO.count(
 +                getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.USER_SEARCH), realms),
 +                searchCondition, AnyTypeKind.USER);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_SEARCH + "')")
 +    @Transactional(readOnly = true, rollbackFor = { Throwable.class })
 +    @Override
 +    public List<UserTO> search(final SearchCond searchCondition, final int page, final int size,
-             final List<OrderByClause> orderBy, final List<String> realms) {
++            final List<OrderByClause> orderBy, final List<String> realms, final boolean details) {
 +
 +        List<User> matchingUsers = searchDAO.search(
 +                getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.USER_SEARCH), realms),
 +                searchCondition, page, size, orderBy, AnyTypeKind.USER);
 +        return CollectionUtils.collect(matchingUsers, new Transformer<User, UserTO>() {
 +
 +            @Override
 +            public UserTO transform(final User input) {
-                 return binder.getUserTO(input);
++                return binder.getUserTO(input, details);
 +            }
 +        }, new ArrayList<UserTO>());
 +    }
 +
 +    @PreAuthorize("isAnonymous() or hasRole('" + Entitlement.ANONYMOUS + "')")
 +    public UserTO createSelf(final UserTO userTO, final boolean storePassword) {
 +        return doCreate(userTO, storePassword);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_CREATE + "')")
 +    @Override
 +    public UserTO create(final UserTO userTO) {
 +        return create(userTO, true);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_CREATE + "')")
 +    public UserTO create(final UserTO userTO, final boolean storePassword) {
 +        if (userTO.getRealm() == null) {
 +            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm);
 +            throw sce;
 +        }
 +        Set<String> effectiveRealms = getEffectiveRealms(
 +                AuthContextUtils.getAuthorizations().get(Entitlement.USER_CREATE),
 +                Collections.singleton(userTO.getRealm()));
 +        if (effectiveRealms.isEmpty()) {
 +            throw new UnauthorizedException(AnyTypeKind.USER, null);
 +        }
 +
 +        return doCreate(userTO, storePassword);
 +    }
 +
 +    protected UserTO doCreate(final UserTO userTO, final boolean storePassword) {
 +        // Any transformation (if configured)
 +        UserTO actual = anyTransformer.transform(userTO);
 +        LOG.debug("Transformed: {}", actual);
 +
 +        Map.Entry<Long, List<PropagationStatus>> created = provisioningManager.create(actual, storePassword);
 +
 +        UserTO savedTO = binder.getUserTO(created.getKey());
 +        savedTO.getPropagationStatusTOs().addAll(created.getValue());
 +        return savedTO;
 +    }
 +
 +    @PreAuthorize("isAuthenticated() and not(hasRole('" + Entitlement.ANONYMOUS + "'))")
 +    public UserTO updateSelf(final UserMod userMod) {
 +        UserTO userTO = binder.getAuthenticatedUserTO();
 +
 +        if (userTO.getKey() != userMod.getKey()) {
 +            throw new AccessControlException("Not allowed for user with key " + userMod.getKey());
 +        }
 +
 +        return update(userMod);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
 +    @Override
 +    public UserTO update(final UserMod userMod) {
 +        // Any transformation (if configured)
 +        UserMod actual = anyTransformer.transform(userMod);
 +        LOG.debug("Transformed: {}", actual);
 +
 +        Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(actual);
 +
 +        UserTO updatedTO = binder.getUserTO(updated.getKey());
 +        updatedTO.getPropagationStatusTOs().addAll(updated.getValue());
 +        return updatedTO;
 +    }
 +
 +    protected Map.Entry<Long, List<PropagationStatus>> setStatusOnWfAdapter(final User user,
 +            final StatusMod statusMod) {
 +        Map.Entry<Long, List<PropagationStatus>> updated;
 +
 +        switch (statusMod.getType()) {
 +            case SUSPEND:
 +                updated = provisioningManager.suspend(user, statusMod);
 +                break;
 +
 +            case REACTIVATE:
 +                updated = provisioningManager.reactivate(user, statusMod);
 +                break;
 +
 +            case ACTIVATE:
 +            default:
 +                updated = provisioningManager.activate(user, statusMod);
 +                break;
 +
 +        }
 +
 +        return updated;
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    public UserTO status(final StatusMod statusMod) {
 +        User user = userDAO.authFind(statusMod.getKey());
 +
 +        Map.Entry<Long, List<PropagationStatus>> updated = setStatusOnWfAdapter(user, statusMod);
 +        final UserTO savedTO = binder.getUserTO(updated.getKey());
 +        savedTO.getPropagationStatusTOs().addAll(updated.getValue());
 +        return savedTO;
 +    }
 +
 +    @PreAuthorize("isAnonymous() or hasRole('" + Entitlement.ANONYMOUS + "')")
 +    @Transactional
 +    public void requestPasswordReset(final String username, final String securityAnswer) {
 +        if (username == null) {
 +            throw new NotFoundException("Null username");
 +        }
 +
 +        User user = userDAO.find(username);
 +        if (user == null) {
 +            throw new NotFoundException("User " + username);
 +        }
 +
 +        if (syncopeLogic.isPwdResetRequiringSecurityQuestions()
 +                && (securityAnswer == null || !securityAnswer.equals(user.getSecurityAnswer()))) {
 +
 +            throw SyncopeClientException.build(ClientExceptionType.InvalidSecurityAnswer);
 +        }
 +
 +        provisioningManager.requestPasswordReset(user.getKey());
 +    }
 +
 +    @PreAuthorize("isAnonymous() or hasRole('" + Entitlement.ANONYMOUS + "')")
 +    @Transactional
 +    public void confirmPasswordReset(final String token, final String password) {
 +        User user = userDAO.findByToken(token);
 +        if (user == null) {
 +            throw new NotFoundException("User with token " + token);
 +        }
 +        provisioningManager.confirmPasswordReset(user, token, password);
 +    }
 +
 +    @PreAuthorize("isAuthenticated() and not(hasRole('" + Entitlement.ANONYMOUS + "'))")
 +    public UserTO deleteSelf() {
 +        UserTO userTO = binder.getAuthenticatedUserTO();
 +
 +        return delete(userTO.getKey());
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_DELETE + "')")
 +    @Override
 +    public UserTO delete(final Long key) {
 +        List<Group> ownedGroups = groupDAO.findOwnedByUser(key);
 +        if (!ownedGroups.isEmpty()) {
 +            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.GroupOwnership);
 +            sce.getElements().addAll(CollectionUtils.collect(ownedGroups, new Transformer<Group, String>() {
 +
 +                @Override
 +                public String transform(final Group group) {
 +                    return group.getKey() + " " + group.getName();
 +                }
 +            }, new ArrayList<String>()));
 +            throw sce;
 +        }
 +
 +        List<PropagationStatus> statuses = provisioningManager.delete(key);
 +
 +        final UserTO deletedTO;
 +        User deleted = userDAO.find(key);
 +        if (deleted == null) {
 +            deletedTO = new UserTO();
 +            deletedTO.setKey(key);
 +        } else {
 +            deletedTO = binder.getUserTO(key);
 +        }
 +        deletedTO.getPropagationStatusTOs().addAll(statuses);
 +
 +        return deletedTO;
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    @Override
 +    public UserTO unlink(final Long key, final Collection<String> resources) {
 +        final UserMod userMod = new UserMod();
 +        userMod.setKey(key);
 +        userMod.getResourcesToRemove().addAll(resources);
 +        Long updatedKey = provisioningManager.unlink(userMod);
 +
 +        return binder.getUserTO(updatedKey);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    @Override
 +    public UserTO link(final Long key, final Collection<String> resources) {
 +        final UserMod userMod = new UserMod();
 +        userMod.setKey(key);
 +        userMod.getResourcesToAdd().addAll(resources);
 +        return binder.getUserTO(provisioningManager.link(userMod));
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    @Override
 +    public UserTO unassign(final Long key, final Collection<String> resources) {
 +        final UserMod userMod = new UserMod();
 +        userMod.setKey(key);
 +        userMod.getResourcesToRemove().addAll(resources);
 +        return update(userMod);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    @Override
 +    public UserTO assign(
 +            final Long key,
 +            final Collection<String> resources,
 +            final boolean changepwd,
 +            final String password) {
 +
 +        final UserMod userMod = new UserMod();
 +        userMod.setKey(key);
 +        userMod.getResourcesToAdd().addAll(resources);
 +
 +        if (changepwd) {
 +            StatusMod statusMod = new StatusMod();
 +            statusMod.setOnSyncope(false);
 +            statusMod.getResourceNames().addAll(resources);
 +            userMod.setPwdPropRequest(statusMod);
 +            userMod.setPassword(password);
 +        }
 +
 +        return update(userMod);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
 +    @Transactional(rollbackFor = { Throwable.class })
 +    @Override
 +    public UserTO deprovision(final Long key, final Collection<String> resources) {
-         final User user = userDAO.authFind(key);
++        User user = userDAO.authFind(key);
 +
 +        List<PropagationStatus> statuses = provisioningManager.deprovision(key, resources);
 +
-         final UserTO updatedUserTO = binder.getUserTO(user);
++        final UserTO updatedUserTO = binder.getUserTO(user, true);
 +        updatedUserTO.getPropagationStatusTOs().addAll(statuses);
 +        return updatedUserTO;
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
 +    @Transactional(readOnly = true)
 +    @Override
 +    public UserTO provision(
 +            final Long key,
 +            final Collection<String> resources,
 +            final boolean changePwd,
 +            final String password) {
 +
 +        final UserTO original = binder.getUserTO(key);
 +
 +        //trick: assign and retrieve propagation statuses ...
 +        original.getPropagationStatusTOs().addAll(
 +                assign(key, resources, changePwd, password).getPropagationStatusTOs());
 +
 +        // .... rollback.
 +        TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
 +        return original;
 +    }
 +
 +    @Override
 +    protected UserTO resolveReference(final Method method, final Object... args) throws UnresolvedReferenceException {
 +        Object key = null;
 +
 +        if (!"confirmPasswordReset".equals(method.getName()) && ArrayUtils.isNotEmpty(args)) {
 +            for (int i = 0; key == null && i < args.length; i++) {
 +                if (args[i] instanceof Long) {
 +                    key = (Long) args[i];
 +                } else if (args[i] instanceof String) {
 +                    key = (String) args[i];
 +                } else if (args[i] instanceof UserTO) {
 +                    key = ((UserTO) args[i]).getKey();
 +                } else if (args[i] instanceof UserMod) {
 +                    key = ((UserMod) args[i]).getKey();
 +                }
 +            }
 +        }
 +
 +        if ((key != null) && !key.equals(0L)) {
 +            try {
 +                return key instanceof Long ? binder.getUserTO((Long) key) : binder.getUserTO((String) key);
 +            } catch (Throwable ignore) {
 +                LOG.debug("Unresolved reference", ignore);
 +                throw new UnresolvedReferenceException(ignore);
 +            }
 +        }
 +
 +        throw new UnresolvedReferenceException();
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
----------------------------------------------------------------------
diff --cc core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
index 72bfb33,0000000..5b89421
mode 100644,000000..100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
@@@ -1,305 -1,0 +1,305 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.logic.report;
 +
 +import java.util.Collection;
 +import java.util.Collections;
 +import java.util.List;
 +import java.util.Map;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.syncope.common.lib.SyncopeConstants;
 +import org.apache.syncope.common.lib.report.GroupReportletConf;
 +import org.apache.syncope.common.lib.report.GroupReportletConf.Feature;
 +import org.apache.syncope.common.lib.to.AnyTO;
 +import org.apache.syncope.common.lib.to.AttrTO;
 +import org.apache.syncope.common.lib.to.GroupTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 +import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 +import org.apache.syncope.core.persistence.api.entity.group.Group;
 +import org.apache.syncope.core.misc.search.SearchCondConverter;
 +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 +import org.apache.syncope.core.persistence.api.entity.user.UMembership;
 +import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.xml.sax.ContentHandler;
 +import org.xml.sax.SAXException;
 +import org.xml.sax.helpers.AttributesImpl;
 +
 +@ReportletConfClass(GroupReportletConf.class)
 +public class GroupReportlet extends AbstractReportlet<GroupReportletConf> {
 +
 +    private static final int PAGE_SIZE = 10;
 +
 +    @Autowired
 +    private GroupDAO groupDAO;
 +
 +    @Autowired
 +    private AnySearchDAO searchDAO;
 +
 +    @Autowired
 +    private GroupDataBinder groupDataBinder;
 +
 +    private List<Group> getPagedGroups(final int page) {
 +        List<Group> result;
 +
 +        if (StringUtils.isBlank(conf.getMatchingCond())) {
 +            result = groupDAO.findAll(SyncopeConstants.FULL_ADMIN_REALMS, page, PAGE_SIZE);
 +        } else {
 +            result = searchDAO.search(SyncopeConstants.FULL_ADMIN_REALMS,
 +                    SearchCondConverter.convert(conf.getMatchingCond()),
 +                    page, PAGE_SIZE, Collections.<OrderByClause>emptyList(), AnyTypeKind.GROUP);
 +        }
 +
 +        return result;
 +    }
 +
 +    private int count() {
 +        return StringUtils.isBlank(conf.getMatchingCond())
 +                ? groupDAO.count(SyncopeConstants.FULL_ADMIN_REALMS)
 +                : searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS,
 +                        SearchCondConverter.convert(conf.getMatchingCond()), AnyTypeKind.GROUP);
 +    }
 +
 +    private void doExtractResources(final ContentHandler handler, final AnyTO anyTO)
 +            throws SAXException {
 +
 +        if (anyTO.getResources().isEmpty()) {
 +            LOG.debug("No resources found for {}[{}]", anyTO.getClass().getSimpleName(), anyTO.getKey());
 +        } else {
 +            AttributesImpl atts = new AttributesImpl();
 +            handler.startElement("", "", "resources", null);
 +
 +            for (String resourceName : anyTO.getResources()) {
 +                atts.clear();
 +
 +                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, resourceName);
 +                handler.startElement("", "", "resource", atts);
 +                handler.endElement("", "", "resource");
 +            }
 +
 +            handler.endElement("", "", "resources");
 +        }
 +    }
 +
 +    private void doExtractAttributes(final ContentHandler handler, final AnyTO anyTO,
 +            final Collection<String> attrs, final Collection<String> derAttrs, final Collection<String> virAttrs)
 +            throws SAXException {
 +
 +        AttributesImpl atts = new AttributesImpl();
 +        if (!attrs.isEmpty()) {
 +            Map<String, AttrTO> attrMap = anyTO.getPlainAttrMap();
 +
 +            handler.startElement("", "", "attributes", null);
 +            for (String attrName : attrs) {
 +                atts.clear();
 +
 +                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
 +                handler.startElement("", "", "attribute", atts);
 +
 +                if (attrMap.containsKey(attrName)) {
 +                    for (String value : attrMap.get(attrName).getValues()) {
 +                        handler.startElement("", "", "value", null);
 +                        handler.characters(value.toCharArray(), 0, value.length());
 +                        handler.endElement("", "", "value");
 +                    }
 +                } else {
 +                    LOG.debug("{} not found for {}[{}]", attrName,
 +                            anyTO.getClass().getSimpleName(), anyTO.getKey());
 +                }
 +
 +                handler.endElement("", "", "attribute");
 +            }
 +            handler.endElement("", "", "attributes");
 +        }
 +
 +        if (!derAttrs.isEmpty()) {
 +            Map<String, AttrTO> derAttrMap = anyTO.getDerAttrMap();
 +
 +            handler.startElement("", "", "derivedAttributes", null);
 +            for (String attrName : derAttrs) {
 +                atts.clear();
 +
 +                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
 +                handler.startElement("", "", "derivedAttribute", atts);
 +
 +                if (derAttrMap.containsKey(attrName)) {
 +                    for (String value : derAttrMap.get(attrName).getValues()) {
 +                        handler.startElement("", "", "value", null);
 +                        handler.characters(value.toCharArray(), 0, value.length());
 +                        handler.endElement("", "", "value");
 +                    }
 +                } else {
 +                    LOG.debug("{} not found for {}[{}]", attrName,
 +                            anyTO.getClass().getSimpleName(), anyTO.getKey());
 +                }
 +
 +                handler.endElement("", "", "derivedAttribute");
 +            }
 +            handler.endElement("", "", "derivedAttributes");
 +        }
 +
 +        if (!virAttrs.isEmpty()) {
 +            Map<String, AttrTO> virAttrMap = anyTO.getVirAttrMap();
 +
 +            handler.startElement("", "", "virtualAttributes", null);
 +            for (String attrName : virAttrs) {
 +                atts.clear();
 +
 +                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
 +                handler.startElement("", "", "virtualAttribute", atts);
 +
 +                if (virAttrMap.containsKey(attrName)) {
 +                    for (String value : virAttrMap.get(attrName).getValues()) {
 +                        handler.startElement("", "", "value", null);
 +                        handler.characters(value.toCharArray(), 0, value.length());
 +                        handler.endElement("", "", "value");
 +                    }
 +                } else {
 +                    LOG.debug("{} not found for {}[{}]", attrName,
 +                            anyTO.getClass().getSimpleName(), anyTO.getKey());
 +                }
 +
 +                handler.endElement("", "", "virtualAttribute");
 +            }
 +            handler.endElement("", "", "virtualAttributes");
 +        }
 +    }
 +
 +    private void doExtract(final ContentHandler handler, final List<Group> groups) throws SAXException {
 +        AttributesImpl atts = new AttributesImpl();
 +        for (Group group : groups) {
 +            atts.clear();
 +
 +            for (Feature feature : conf.getFeatures()) {
 +                String type = null;
 +                String value = null;
 +                switch (feature) {
 +                    case key:
 +                        type = ReportXMLConst.XSD_LONG;
 +                        value = String.valueOf(group.getKey());
 +                        break;
 +
 +                    case name:
 +                        type = ReportXMLConst.XSD_STRING;
 +                        value = String.valueOf(group.getName());
 +                        break;
 +
 +                    case groupOwner:
 +                        type = ReportXMLConst.XSD_LONG;
 +                        value = String.valueOf(group.getGroupOwner());
 +                        break;
 +
 +                    case userOwner:
 +                        type = ReportXMLConst.XSD_LONG;
 +                        value = String.valueOf(group.getUserOwner());
 +                        break;
 +
 +                    default:
 +                }
 +
 +                if (type != null && value != null) {
 +                    atts.addAttribute("", "", feature.name(), type, value);
 +                }
 +            }
 +
 +            handler.startElement("", "", "group", atts);
 +
 +            // Using GroupTO for attribute values, since the conversion logic of
 +            // values to String is already encapsulated there
-             GroupTO groupTO = groupDataBinder.getGroupTO(group);
++            GroupTO groupTO = groupDataBinder.getGroupTO(group, true);
 +
 +            doExtractAttributes(handler, groupTO, conf.getPlainAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
 +
 +            // to get resources associated to a group
 +            if (conf.getFeatures().contains(Feature.resources)) {
 +                doExtractResources(handler, groupTO);
 +            }
 +            //to get users asscoiated to a group is preferred GroupDAO to GroupTO
 +            if (conf.getFeatures().contains(Feature.users)) {
 +                handler.startElement("", "", "users", null);
 +
 +                for (UMembership memb : groupDAO.findUMemberships(group)) {
 +                    atts.clear();
 +
 +                    atts.addAttribute("", "", "key", ReportXMLConst.XSD_LONG,
 +                            String.valueOf(memb.getLeftEnd().getKey()));
 +                    atts.addAttribute("", "", "username", ReportXMLConst.XSD_STRING,
 +                            String.valueOf(memb.getLeftEnd().getUsername()));
 +
 +                    handler.startElement("", "", "user", atts);
 +                    handler.endElement("", "", "user");
 +                }
 +
 +                handler.endElement("", "", "users");
 +            }
 +
 +            handler.endElement("", "", "group");
 +        }
 +    }
 +
 +    private void doExtractConf(final ContentHandler handler) throws SAXException {
 +        if (conf == null) {
 +            LOG.debug("Report configuration is not present");
 +        }
 +
 +        AttributesImpl atts = new AttributesImpl();
 +        handler.startElement("", "", "configurations", null);
 +        handler.startElement("", "", "groupAttributes", atts);
 +
 +        for (Feature feature : conf.getFeatures()) {
 +            atts.clear();
 +            handler.startElement("", "", "feature", atts);
 +            handler.characters(feature.name().toCharArray(), 0, feature.name().length());
 +            handler.endElement("", "", "feature");
 +        }
 +
 +        for (String attr : conf.getPlainAttrs()) {
 +            atts.clear();
 +            handler.startElement("", "", "attribute", atts);
 +            handler.characters(attr.toCharArray(), 0, attr.length());
 +            handler.endElement("", "", "attribute");
 +        }
 +
 +        for (String derAttr : conf.getDerAttrs()) {
 +            atts.clear();
 +            handler.startElement("", "", "derAttribute", atts);
 +            handler.characters(derAttr.toCharArray(), 0, derAttr.length());
 +            handler.endElement("", "", "derAttribute");
 +        }
 +
 +        for (String virAttr : conf.getVirAttrs()) {
 +            atts.clear();
 +            handler.startElement("", "", "virAttribute", atts);
 +            handler.characters(virAttr.toCharArray(), 0, virAttr.length());
 +            handler.endElement("", "", "virAttribute");
 +        }
 +
 +        handler.endElement("", "", "groupAttributes");
 +        handler.endElement("", "", "configurations");
 +    }
 +
 +    @Override
 +    protected void doExtract(final ContentHandler handler) throws SAXException {
 +        doExtractConf(handler);
 +        for (int i = 1; i <= (count() / PAGE_SIZE) + 1; i++) {
 +            doExtract(handler, getPagedGroups(i));
 +        }
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
----------------------------------------------------------------------
diff --cc core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
index b05c4e6,0000000..b626dd1
mode 100644,000000..100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
@@@ -1,378 -1,0 +1,379 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.logic.report;
 +
 +import java.util.Collection;
 +import java.util.Collections;
 +import java.util.List;
 +import java.util.Map;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.syncope.common.lib.SyncopeConstants;
 +import org.apache.syncope.common.lib.report.UserReportletConf;
 +import org.apache.syncope.common.lib.report.UserReportletConf.Feature;
 +import org.apache.syncope.common.lib.to.AnyTO;
 +import org.apache.syncope.common.lib.to.AttrTO;
 +import org.apache.syncope.common.lib.to.MembershipTO;
 +import org.apache.syncope.common.lib.to.RelationshipTO;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.core.persistence.api.dao.UserDAO;
 +import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.core.misc.search.SearchCondConverter;
 +import org.apache.syncope.core.misc.DataFormat;
 +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 +import org.apache.syncope.core.persistence.api.entity.user.UMembership;
 +import org.apache.syncope.core.persistence.api.entity.user.URelationship;
 +import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
 +import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
 +import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.xml.sax.ContentHandler;
 +import org.xml.sax.SAXException;
 +import org.xml.sax.helpers.AttributesImpl;
 +
 +@ReportletConfClass(UserReportletConf.class)
 +public class UserReportlet extends AbstractReportlet<UserReportletConf> {
 +
 +    private static final int PAGE_SIZE = 10;
 +
 +    @Autowired
 +    private UserDAO userDAO;
 +
 +    @Autowired
 +    private AnySearchDAO searchDAO;
 +
 +    @Autowired
 +    private UserDataBinder userDataBinder;
 +
 +    @Autowired
 +    private GroupDataBinder groupDataBinder;
 +
 +    @Autowired
 +    private AnyObjectDataBinder anyObjectDataBinder;
 +
 +    private List<User> getPagedUsers(final int page) {
 +        List<User> result;
 +
 +        if (StringUtils.isBlank(conf.getMatchingCond())) {
 +            result = userDAO.findAll(SyncopeConstants.FULL_ADMIN_REALMS, page, PAGE_SIZE);
 +        } else {
 +            result = searchDAO.search(SyncopeConstants.FULL_ADMIN_REALMS,
 +                    SearchCondConverter.convert(conf.getMatchingCond()),
 +                    page, PAGE_SIZE, Collections.<OrderByClause>emptyList(), AnyTypeKind.USER);
 +        }
 +
 +        return result;
 +    }
 +
 +    private int count() {
 +        return StringUtils.isBlank(conf.getMatchingCond())
 +                ? userDAO.count(SyncopeConstants.FULL_ADMIN_REALMS)
 +                : searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS,
 +                        SearchCondConverter.convert(conf.getMatchingCond()), AnyTypeKind.USER);
 +    }
 +
 +    private void doExtractResources(final ContentHandler handler, final AnyTO anyTO)
 +            throws SAXException {
 +
 +        if (anyTO.getResources().isEmpty()) {
 +            LOG.debug("No resources found for {}[{}]", anyTO.getClass().getSimpleName(), anyTO.getKey());
 +        } else {
 +            AttributesImpl atts = new AttributesImpl();
 +            handler.startElement("", "", "resources", null);
 +
 +            for (String resourceName : anyTO.getResources()) {
 +                atts.clear();
 +
 +                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, resourceName);
 +                handler.startElement("", "", "resource", atts);
 +                handler.endElement("", "", "resource");
 +            }
 +
 +            handler.endElement("", "", "resources");
 +        }
 +    }
 +
 +    private void doExtractAttributes(final ContentHandler handler, final AnyTO anyTO,
 +            final Collection<String> attrs, final Collection<String> derAttrs, final Collection<String> virAttrs)
 +            throws SAXException {
 +
 +        AttributesImpl atts = new AttributesImpl();
 +        if (!attrs.isEmpty()) {
 +            Map<String, AttrTO> attrMap = anyTO.getPlainAttrMap();
 +
 +            handler.startElement("", "", "attributes", null);
 +            for (String attrName : attrs) {
 +                atts.clear();
 +
 +                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
 +                handler.startElement("", "", "attribute", atts);
 +
 +                if (attrMap.containsKey(attrName)) {
 +                    for (String value : attrMap.get(attrName).getValues()) {
 +                        handler.startElement("", "", "value", null);
 +                        handler.characters(value.toCharArray(), 0, value.length());
 +                        handler.endElement("", "", "value");
 +                    }
 +                } else {
 +                    LOG.debug("{} not found for {}[{}]", attrName,
 +                            anyTO.getClass().getSimpleName(), anyTO.getKey());
 +                }
 +
 +                handler.endElement("", "", "attribute");
 +            }
 +            handler.endElement("", "", "attributes");
 +        }
 +
 +        if (!derAttrs.isEmpty()) {
 +            Map<String, AttrTO> derAttrMap = anyTO.getDerAttrMap();
 +
 +            handler.startElement("", "", "derivedAttributes", null);
 +            for (String attrName : derAttrs) {
 +                atts.clear();
 +
 +                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
 +                handler.startElement("", "", "derivedAttribute", atts);
 +
 +                if (derAttrMap.containsKey(attrName)) {
 +                    for (String value : derAttrMap.get(attrName).getValues()) {
 +                        handler.startElement("", "", "value", null);
 +                        handler.characters(value.toCharArray(), 0, value.length());
 +                        handler.endElement("", "", "value");
 +                    }
 +                } else {
 +                    LOG.debug("{} not found for {}[{}]", attrName,
 +                            anyTO.getClass().getSimpleName(), anyTO.getKey());
 +                }
 +
 +                handler.endElement("", "", "derivedAttribute");
 +            }
 +            handler.endElement("", "", "derivedAttributes");
 +        }
 +
 +        if (!virAttrs.isEmpty()) {
 +            Map<String, AttrTO> virAttrMap = anyTO.getVirAttrMap();
 +
 +            handler.startElement("", "", "virtualAttributes", null);
 +            for (String attrName : virAttrs) {
 +                atts.clear();
 +
 +                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
 +                handler.startElement("", "", "virtualAttribute", atts);
 +
 +                if (virAttrMap.containsKey(attrName)) {
 +                    for (String value : virAttrMap.get(attrName).getValues()) {
 +                        handler.startElement("", "", "value", null);
 +                        handler.characters(value.toCharArray(), 0, value.length());
 +                        handler.endElement("", "", "value");
 +                    }
 +                } else {
 +                    LOG.debug("{} not found for {}[{}]", attrName,
 +                            anyTO.getClass().getSimpleName(), anyTO.getKey());
 +                }
 +
 +                handler.endElement("", "", "virtualAttribute");
 +            }
 +            handler.endElement("", "", "virtualAttributes");
 +        }
 +    }
 +
 +    private void doExtract(final ContentHandler handler, final List<User> users) throws SAXException {
 +        AttributesImpl atts = new AttributesImpl();
 +        for (User user : users) {
 +            atts.clear();
 +
 +            for (Feature feature : conf.getFeatures()) {
 +                String type = null;
 +                String value = null;
 +                switch (feature) {
 +                    case key:
 +                        type = ReportXMLConst.XSD_LONG;
 +                        value = String.valueOf(user.getKey());
 +                        break;
 +
 +                    case username:
 +                        type = ReportXMLConst.XSD_STRING;
 +                        value = user.getUsername();
 +                        break;
 +
 +                    case workflowId:
 +                        type = ReportXMLConst.XSD_LONG;
 +                        value = String.valueOf(user.getWorkflowId());
 +                        break;
 +
 +                    case status:
 +                        type = ReportXMLConst.XSD_STRING;
 +                        value = user.getStatus();
 +                        break;
 +
 +                    case creationDate:
 +                        type = ReportXMLConst.XSD_DATETIME;
 +                        value = user.getCreationDate() == null
 +                                ? ""
 +                                : DataFormat.format(user.getCreationDate());
 +                        break;
 +
 +                    case lastLoginDate:
 +                        type = ReportXMLConst.XSD_DATETIME;
 +                        value = user.getLastLoginDate() == null
 +                                ? ""
 +                                : DataFormat.format(user.getLastLoginDate());
 +                        break;
 +
 +                    case changePwdDate:
 +                        type = ReportXMLConst.XSD_DATETIME;
 +                        value = user.getChangePwdDate() == null
 +                                ? ""
 +                                : DataFormat.format(user.getChangePwdDate());
 +                        break;
 +
 +                    case passwordHistorySize:
 +                        type = ReportXMLConst.XSD_INT;
 +                        value = String.valueOf(user.getPasswordHistory().size());
 +                        break;
 +
 +                    case failedLoginCount:
 +                        type = ReportXMLConst.XSD_INT;
 +                        value = String.valueOf(user.getFailedLogins());
 +                        break;
 +
 +                    default:
 +                }
 +
 +                if (type != null && value != null) {
 +                    atts.addAttribute("", "", feature.name(), type, value);
 +                }
 +            }
 +
 +            handler.startElement("", "", "user", atts);
 +
 +            // Using UserTO for attribute values, since the conversion logic of
 +            // values to String is already encapsulated there
-             UserTO userTO = userDataBinder.getUserTO(user);
++            UserTO userTO = userDataBinder.getUserTO(user, true);
 +
 +            doExtractAttributes(handler, userTO, conf.getPlainAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
 +
 +            if (conf.getFeatures().contains(Feature.relationships)) {
 +                handler.startElement("", "", "relationships", null);
 +
 +                for (RelationshipTO rel : userTO.getRelationships()) {
 +                    atts.clear();
 +
 +                    atts.addAttribute("", "", "anyObjectKey",
 +                            ReportXMLConst.XSD_LONG, String.valueOf(rel.getRightKey()));
 +                    handler.startElement("", "", "relationship", atts);
 +
 +                    if (conf.getFeatures().contains(Feature.resources)) {
 +                        URelationship actualRel = user.getRelationship(rel.getRightKey());
 +                        if (actualRel == null) {
 +                            LOG.warn("Unexpected: cannot find relationship for any object {} for user {}",
 +                                    rel.getRightKey(), user);
 +                        } else {
-                             doExtractResources(handler, anyObjectDataBinder.getAnyObjectTO(actualRel.getRightEnd()));
++                            doExtractResources(
++                                    handler, anyObjectDataBinder.getAnyObjectTO(actualRel.getRightEnd(), true));
 +                        }
 +                    }
 +
 +                    handler.endElement("", "", "relationship");
 +                }
 +
 +                handler.endElement("", "", "relationships");
 +            }
 +            if (conf.getFeatures().contains(Feature.memberships)) {
 +                handler.startElement("", "", "memberships", null);
 +
 +                for (MembershipTO memb : userTO.getMemberships()) {
 +                    atts.clear();
 +
 +                    atts.addAttribute("", "", "groupKey",
 +                            ReportXMLConst.XSD_LONG, String.valueOf(memb.getRightKey()));
 +                    atts.addAttribute("", "", "groupName", ReportXMLConst.XSD_STRING, String.
 +                            valueOf(memb.getGroupName()));
 +                    handler.startElement("", "", "membership", atts);
 +
 +                    if (conf.getFeatures().contains(Feature.resources)) {
 +                        UMembership actualMemb = user.getMembership(memb.getRightKey());
 +                        if (actualMemb == null) {
 +                            LOG.warn("Unexpected: cannot find membership for group {} for user {}",
 +                                    memb.getRightKey(), user);
 +                        } else {
-                             doExtractResources(handler, groupDataBinder.getGroupTO(actualMemb.getRightEnd()));
++                            doExtractResources(handler, groupDataBinder.getGroupTO(actualMemb.getRightEnd(), true));
 +                        }
 +                    }
 +
 +                    handler.endElement("", "", "membership");
 +                }
 +
 +                handler.endElement("", "", "memberships");
 +            }
 +
 +            if (conf.getFeatures().contains(Feature.resources)) {
 +                doExtractResources(handler, userTO);
 +            }
 +
 +            handler.endElement("", "", "user");
 +        }
 +    }
 +
 +    private void doExtractConf(final ContentHandler handler) throws SAXException {
 +        AttributesImpl atts = new AttributesImpl();
 +        handler.startElement("", "", "configurations", null);
 +        handler.startElement("", "", "userAttributes", atts);
 +
 +        for (Feature feature : conf.getFeatures()) {
 +            atts.clear();
 +            handler.startElement("", "", "feature", atts);
 +            handler.characters(feature.name().toCharArray(), 0, feature.name().length());
 +            handler.endElement("", "", "feature");
 +        }
 +
 +        for (String attr : conf.getPlainAttrs()) {
 +            atts.clear();
 +            handler.startElement("", "", "attribute", atts);
 +            handler.characters(attr.toCharArray(), 0, attr.length());
 +            handler.endElement("", "", "attribute");
 +        }
 +
 +        for (String derAttr : conf.getDerAttrs()) {
 +            atts.clear();
 +            handler.startElement("", "", "derAttribute", atts);
 +            handler.characters(derAttr.toCharArray(), 0, derAttr.length());
 +            handler.endElement("", "", "derAttribute");
 +        }
 +
 +        for (String virAttr : conf.getVirAttrs()) {
 +            atts.clear();
 +            handler.startElement("", "", "virAttribute", atts);
 +            handler.characters(virAttr.toCharArray(), 0, virAttr.length());
 +            handler.endElement("", "", "virAttribute");
 +        }
 +
 +        handler.endElement("", "", "userAttributes");
 +        handler.endElement("", "", "configurations");
 +    }
 +
 +    @Override
 +    protected void doExtract(final ContentHandler handler) throws SAXException {
 +        doExtractConf(handler);
 +        for (int i = 1; i <= (count() / PAGE_SIZE) + 1; i++) {
 +            doExtract(handler, getPagedUsers(i));
 +        }
 +    }
 +}


[3/7] syncope git commit: [SYNCOPE-676] Merge from 1_2_X

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
----------------------------------------------------------------------
diff --cc core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
index 2e00cb9,0000000..e44a66f
mode 100644,000000..100644
--- a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
+++ b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
@@@ -1,878 -1,0 +1,878 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.workflow.activiti;
 +
 +import com.fasterxml.jackson.databind.JsonNode;
 +import com.fasterxml.jackson.databind.ObjectMapper;
 +import com.fasterxml.jackson.databind.node.ObjectNode;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.io.OutputStream;
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.HashSet;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +import javax.annotation.Resource;
 +import javax.ws.rs.NotFoundException;
 +import org.activiti.bpmn.converter.BpmnXMLConverter;
 +import org.activiti.bpmn.model.BpmnModel;
 +import org.activiti.editor.constants.ModelDataJsonConstants;
 +import org.activiti.editor.language.json.converter.BpmnJsonConverter;
 +import org.activiti.engine.ActivitiException;
 +import org.activiti.engine.FormService;
 +import org.activiti.engine.HistoryService;
 +import org.activiti.engine.RepositoryService;
 +import org.activiti.engine.RuntimeService;
 +import org.activiti.engine.TaskService;
 +import org.activiti.engine.form.FormProperty;
 +import org.activiti.engine.form.FormType;
 +import org.activiti.engine.form.TaskFormData;
 +import org.activiti.engine.history.HistoricActivityInstance;
 +import org.activiti.engine.history.HistoricDetail;
 +import org.activiti.engine.history.HistoricTaskInstance;
 +import org.activiti.engine.impl.persistence.entity.HistoricFormPropertyEntity;
 +import org.activiti.engine.query.Query;
 +import org.activiti.engine.repository.Model;
 +import org.activiti.engine.repository.ProcessDefinition;
 +import org.activiti.engine.runtime.ProcessInstance;
 +import org.activiti.engine.task.Task;
 +import org.apache.commons.io.IOUtils;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.commons.lang3.tuple.ImmutablePair;
 +import org.apache.commons.lang3.tuple.Pair;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +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.PropagationByResource;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.common.lib.types.WorkflowFormPropertyType;
 +import org.apache.syncope.core.misc.security.AuthContextUtils;
 +import org.apache.syncope.core.misc.spring.BeanUtils;
 +import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
 +import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.core.provisioning.api.WorkflowResult;
 +import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 +import org.apache.syncope.core.workflow.api.WorkflowDefinitionFormat;
 +import org.apache.syncope.core.workflow.api.WorkflowException;
 +import org.apache.syncope.core.workflow.java.AbstractUserWorkflowAdapter;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.transaction.annotation.Transactional;
 +
 +/**
 + * Activiti {@link http://www.activiti.org/} based implementation.
 + */
 +public class ActivitiUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
 +
 +    private static final Logger LOG = LoggerFactory.getLogger(ActivitiUserWorkflowAdapter.class);
 +
 +    private static final String[] PROPERTY_IGNORE_PROPS = { "type" };
 +
 +    public static final String WF_PROCESS_ID = "userWorkflow";
 +
 +    public static final String WF_PROCESS_RESOURCE = "userWorkflow.bpmn20.xml";
 +
 +    public static final String WF_DGRM_RESOURCE = "userWorkflow.userWorkflow.png";
 +
 +    public static final String USER = "user";
 +
 +    public static final String WF_EXECUTOR = "wfExecutor";
 +
 +    public static final String FORM_SUBMITTER = "formSubmitter";
 +
 +    public static final String USER_TO = "userTO";
 +
 +    public static final String ENABLED = "enabled";
 +
 +    public static final String USER_MOD = "userMod";
 +
 +    public static final String EMAIL_KIND = "emailKind";
 +
 +    public static final String TASK = "task";
 +
 +    public static final String TOKEN = "token";
 +
 +    public static final String PASSWORD = "password";
 +
 +    public static final String PROP_BY_RESOURCE = "propByResource";
 +
 +    public static final String PROPAGATE_ENABLE = "propagateEnable";
 +
 +    public static final String ENCRYPTED_PWD = "encryptedPwd";
 +
 +    public static final String TASK_IS_FORM = "taskIsForm";
 +
 +    public static final String MODEL_DATA_JSON_MODEL = "model";
 +
 +    public static final String STORE_PASSWORD = "storePassword";
 +
 +    public static final String EVENT = "event";
 +
 +    @Resource(name = "adminUser")
 +    private String adminUser;
 +
 +    @Autowired
 +    private RuntimeService runtimeService;
 +
 +    @Autowired
 +    private TaskService taskService;
 +
 +    @Autowired
 +    private FormService formService;
 +
 +    @Autowired
 +    private HistoryService historyService;
 +
 +    @Autowired
 +    private RepositoryService repositoryService;
 +
 +    @Autowired
 +    private ActivitiImportUtils importUtils;
 +
 +    @Autowired
 +    private UserDataBinder userDataBinder;
 +
 +    @Override
 +    public String getPrefix() {
 +        return "ACT_";
 +    }
 +
 +    private void throwException(final ActivitiException e, final String defaultMessage) {
 +        if (e.getCause() != null) {
 +            if (e.getCause().getCause() instanceof SyncopeClientException) {
 +                throw (SyncopeClientException) e.getCause().getCause();
 +            } else if (e.getCause().getCause() instanceof ParsingValidationException) {
 +                throw (ParsingValidationException) e.getCause().getCause();
 +            } else if (e.getCause().getCause() instanceof InvalidEntityException) {
 +                throw (InvalidEntityException) e.getCause().getCause();
 +            }
 +        }
 +
 +        throw new WorkflowException(defaultMessage, e);
 +    }
 +
 +    private void updateStatus(final User user) {
 +        List<Task> tasks = taskService.createTaskQuery().processInstanceId(user.getWorkflowId()).list();
 +        if (tasks.isEmpty() || tasks.size() > 1) {
 +            LOG.warn("While setting user status: unexpected task number ({})", tasks.size());
 +        } else {
 +            user.setStatus(tasks.get(0).getTaskDefinitionKey());
 +        }
 +    }
 +
 +    private String getFormTask(final User user) {
 +        String result = null;
 +
 +        List<Task> tasks = taskService.createTaskQuery().processInstanceId(user.getWorkflowId()).list();
 +        if (tasks.isEmpty() || tasks.size() > 1) {
 +            LOG.warn("While checking if form task: unexpected task number ({})", tasks.size());
 +        } else {
 +            try {
 +                TaskFormData formData = formService.getTaskFormData(tasks.get(0).getId());
 +                if (formData != null && !formData.getFormProperties().isEmpty()) {
 +                    result = tasks.get(0).getId();
 +                }
 +            } catch (ActivitiException e) {
 +                LOG.warn("Could not get task form data", e);
 +            }
 +        }
 +
 +        return result;
 +    }
 +
 +    private Set<String> getPerformedTasks(final User user) {
 +        final Set<String> result = new HashSet<>();
 +
 +        for (HistoricActivityInstance task
 +                : historyService.createHistoricActivityInstanceQuery().executionId(user.getWorkflowId()).list()) {
 +
 +            result.add(task.getActivityId());
 +        }
 +
 +        return result;
 +    }
 +
 +    /**
 +     * Saves resources to be propagated and password for later - after form submission - propagation.
 +     */
 +    private void saveForFormSubmit(final User user, final String password,
 +            final PropagationByResource propByRes) {
 +
 +        String formTaskId = getFormTask(user);
 +        if (formTaskId != null) {
 +            // SYNCOPE-238: This is needed to simplify the task query in this.getForms()
 +            taskService.setVariableLocal(formTaskId, TASK_IS_FORM, Boolean.TRUE);
 +            runtimeService.setVariable(user.getWorkflowId(), PROP_BY_RESOURCE, propByRes);
 +            if (propByRes != null) {
 +                propByRes.clear();
 +            }
 +
 +            if (StringUtils.isNotBlank(password)) {
 +                runtimeService.setVariable(user.getWorkflowId(), ENCRYPTED_PWD, encrypt(password));
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public WorkflowResult<Pair<Long, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
 +            final boolean storePassword) {
 +
 +        return create(userTO, disablePwdPolicyCheck, null, storePassword);
 +    }
 +
 +    @Override
 +    public WorkflowResult<Pair<Long, Boolean>> create(final UserTO userTO, final boolean storePassword) {
 +        return create(userTO, false, storePassword);
 +    }
 +
 +    @Override
 +    public WorkflowResult<Pair<Long, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
 +            final Boolean enabled, final boolean storePassword) {
 +
 +        Map<String, Object> variables = new HashMap<>();
 +        variables.put(WF_EXECUTOR, AuthContextUtils.getAuthenticatedUsername());
 +        variables.put(USER_TO, userTO);
 +        variables.put(ENABLED, enabled);
 +        variables.put(STORE_PASSWORD, storePassword);
 +
 +        ProcessInstance processInstance = null;
 +        try {
 +            processInstance = runtimeService.startProcessInstanceByKey(WF_PROCESS_ID, variables);
 +        } catch (ActivitiException e) {
 +            throwException(e, "While starting " + WF_PROCESS_ID + " instance");
 +        }
 +
 +        User user = runtimeService.getVariable(processInstance.getProcessInstanceId(), USER, User.class);
 +
 +        Boolean updatedEnabled =
 +                runtimeService.getVariable(processInstance.getProcessInstanceId(), ENABLED, Boolean.class);
 +        if (updatedEnabled != null) {
 +            user.setSuspended(!updatedEnabled);
 +        }
 +
 +        // this will make UserValidator not to consider password policies at all
 +        if (disablePwdPolicyCheck) {
 +            user.removeClearPassword();
 +        }
 +
 +        updateStatus(user);
 +        user = userDAO.save(user);
 +
 +        Boolean propagateEnable =
 +                runtimeService.getVariable(processInstance.getProcessInstanceId(), PROPAGATE_ENABLE, Boolean.class);
 +        if (propagateEnable == null) {
 +            propagateEnable = enabled;
 +        }
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.set(ResourceOperation.CREATE, userDAO.findAllResourceNames(user));
 +
 +        saveForFormSubmit(user, userTO.getPassword(), propByRes);
 +
 +        return new WorkflowResult<Pair<Long, Boolean>>(
 +                new ImmutablePair<>(user.getKey(), propagateEnable), propByRes, getPerformedTasks(user));
 +    }
 +
 +    private Set<String> doExecuteTask(final User user, final String task, final Map<String, Object> moreVariables) {
 +        Set<String> preTasks = getPerformedTasks(user);
 +
 +        final Map<String, Object> variables = new HashMap<>();
 +        variables.put(WF_EXECUTOR, AuthContextUtils.getAuthenticatedUsername());
 +        variables.put(TASK, task);
 +
 +        // using BeanUtils to access all user's properties and trigger lazy loading - we are about to
 +        // serialize a User instance for availability within workflow tasks, and this breaks transactions
 +        BeanUtils.copyProperties(user, entityFactory.newEntity(User.class));
 +        variables.put(USER, user);
 +
 +        if (moreVariables != null && !moreVariables.isEmpty()) {
 +            variables.putAll(moreVariables);
 +        }
 +
 +        if (StringUtils.isBlank(user.getWorkflowId())) {
 +            throw new WorkflowException(new NotFoundException("Empty workflow id for " + user));
 +        }
 +
 +        List<Task> tasks = taskService.createTaskQuery().processInstanceId(user.getWorkflowId()).list();
 +        if (tasks.size() == 1) {
 +            try {
 +                taskService.complete(tasks.get(0).getId(), variables);
 +            } catch (ActivitiException e) {
 +                throwException(e, "While completing task '" + tasks.get(0).getName() + "' for " + user);
 +            }
 +        } else {
 +            LOG.warn("Expected a single task, found {}", tasks.size());
 +        }
 +
 +        Set<String> postTasks = getPerformedTasks(user);
 +        postTasks.removeAll(preTasks);
 +        postTasks.add(task);
 +        return postTasks;
 +    }
 +
 +    @Override
 +    protected WorkflowResult<Long> doActivate(final User user, final String token) {
 +        Set<String> tasks = doExecuteTask(user, "activate", Collections.singletonMap(TOKEN, (Object) token));
 +
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        return new WorkflowResult<>(updated.getKey(), null, tasks);
 +    }
 +
 +    @Override
 +    protected WorkflowResult<Pair<UserMod, Boolean>> doUpdate(final User user, final UserMod userMod) {
 +        Set<String> tasks = doExecuteTask(user, "update", Collections.singletonMap(USER_MOD, (Object) userMod));
 +
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        PropagationByResource propByRes =
 +                runtimeService.getVariable(user.getWorkflowId(), PROP_BY_RESOURCE, PropagationByResource.class);
 +        UserMod updatedMod =
 +                runtimeService.getVariable(user.getWorkflowId(), USER_MOD, UserMod.class);
 +
 +        saveForFormSubmit(updated, updatedMod.getPassword(), propByRes);
 +
 +        Boolean propagateEnable = runtimeService.getVariable(user.getWorkflowId(), PROPAGATE_ENABLE, Boolean.class);
 +
 +        return new WorkflowResult<Pair<UserMod, Boolean>>(
 +                new ImmutablePair<>(updatedMod, propagateEnable), propByRes, tasks);
 +    }
 +
 +    @Override
 +    @Transactional(rollbackFor = { Throwable.class })
 +    protected WorkflowResult<Long> doSuspend(final User user) {
 +        Set<String> performedTasks = doExecuteTask(user, "suspend", null);
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        return new WorkflowResult<>(updated.getKey(), null, performedTasks);
 +    }
 +
 +    @Override
 +    protected WorkflowResult<Long> doReactivate(final User user) {
 +        Set<String> performedTasks = doExecuteTask(user, "reactivate", null);
 +        updateStatus(user);
 +
 +        User updated = userDAO.save(user);
 +
 +        return new WorkflowResult<>(updated.getKey(), null, performedTasks);
 +    }
 +
 +    @Override
 +    protected void doRequestPasswordReset(final User user) {
 +        Map<String, Object> variables = new HashMap<>(2);
-         variables.put(USER_TO, userDataBinder.getUserTO(user));
++        variables.put(USER_TO, userDataBinder.getUserTO(user, true));
 +        variables.put(EVENT, "requestPasswordReset");
 +
 +        doExecuteTask(user, "requestPasswordReset", variables);
 +        userDAO.save(user);
 +    }
 +
 +    @Override
 +    protected void doConfirmPasswordReset(final User user, final String token, final String password) {
 +        Map<String, Object> variables = new HashMap<>(4);
 +        variables.put(TOKEN, token);
 +        variables.put(PASSWORD, password);
-         variables.put(USER_TO, userDataBinder.getUserTO(user));
++        variables.put(USER_TO, userDataBinder.getUserTO(user, true));
 +        variables.put(EVENT, "confirmPasswordReset");
 +
 +        doExecuteTask(user, "confirmPasswordReset", variables);
 +        userDAO.save(user);
 +    }
 +
 +    @Override
 +    protected void doDelete(final User user) {
 +        doExecuteTask(user, "delete", null);
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.set(ResourceOperation.DELETE, userDAO.findAllResourceNames(user));
 +
 +        saveForFormSubmit(user, null, propByRes);
 +
 +        if (runtimeService.createProcessInstanceQuery().
 +                processInstanceId(user.getWorkflowId()).active().list().isEmpty()) {
 +
 +            userDAO.delete(user.getKey());
 +
 +            if (!historyService.createHistoricProcessInstanceQuery().
 +                    processInstanceId(user.getWorkflowId()).list().isEmpty()) {
 +
 +                historyService.deleteHistoricProcessInstance(user.getWorkflowId());
 +            }
 +        } else {
 +            updateStatus(user);
 +            userDAO.save(user);
 +        }
 +    }
 +
 +    @Override
 +    public WorkflowResult<Long> execute(final UserTO userTO, final String taskId) {
 +        User user = userDAO.authFind(userTO.getKey());
 +
 +        final Map<String, Object> variables = new HashMap<>();
 +        variables.put(USER_TO, userTO);
 +
 +        Set<String> performedTasks = doExecuteTask(user, taskId, variables);
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        return new WorkflowResult<>(updated.getKey(), null, performedTasks);
 +    }
 +
 +    protected ProcessDefinition getProcessDefinition() {
 +        try {
 +            return repositoryService.createProcessDefinitionQuery().processDefinitionKey(
 +                    ActivitiUserWorkflowAdapter.WF_PROCESS_ID).latestVersion().singleResult();
 +        } catch (ActivitiException e) {
 +            throw new WorkflowException("While accessing process " + ActivitiUserWorkflowAdapter.WF_PROCESS_ID, e);
 +        }
 +
 +    }
 +
 +    protected Model getModel(final ProcessDefinition procDef) {
 +        try {
 +            Model model = repositoryService.createModelQuery().deploymentId(procDef.getDeploymentId()).singleResult();
 +            if (model == null) {
 +                throw new NotFoundException("Could not find Model for deployment " + procDef.getDeploymentId());
 +            }
 +            return model;
 +        } catch (Exception e) {
 +            throw new WorkflowException("While accessing process " + ActivitiUserWorkflowAdapter.WF_PROCESS_ID, e);
 +        }
 +    }
 +
 +    protected void exportProcessResource(final String resourceName, final OutputStream os) {
 +        ProcessDefinition procDef = getProcessDefinition();
 +
 +        InputStream procDefIS = repositoryService.getResourceAsStream(procDef.getDeploymentId(), resourceName);
 +        try {
 +            IOUtils.copy(procDefIS, os);
 +        } catch (IOException e) {
 +            LOG.error("While exporting workflow definition {}", procDef.getKey(), e);
 +        } finally {
 +            IOUtils.closeQuietly(procDefIS);
 +        }
 +    }
 +
 +    protected void exportProcessModel(final OutputStream os) {
 +        Model model = getModel(getProcessDefinition());
 +
 +        ObjectMapper objectMapper = new ObjectMapper();
 +        try {
 +            ObjectNode modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
 +            modelNode.put(ModelDataJsonConstants.MODEL_ID, model.getId());
 +            modelNode.replace(MODEL_DATA_JSON_MODEL,
 +                    objectMapper.readTree(repositoryService.getModelEditorSource(model.getId())));
 +
 +            os.write(modelNode.toString().getBytes());
 +        } catch (IOException e) {
 +            LOG.error("While exporting workflow definition {}", model.getId(), e);
 +        }
 +    }
 +
 +    @Override
 +    public void exportDefinition(final WorkflowDefinitionFormat format, final OutputStream os) {
 +        switch (format) {
 +            case JSON:
 +                exportProcessModel(os);
 +                break;
 +
 +            case XML:
 +            default:
 +                exportProcessResource(WF_PROCESS_RESOURCE, os);
 +        }
 +    }
 +
 +    @Override
 +    public void exportDiagram(final OutputStream os) {
 +        exportProcessResource(WF_DGRM_RESOURCE, os);
 +    }
 +
 +    @Override
 +    public void importDefinition(final WorkflowDefinitionFormat format, final String definition) {
 +        Model model = getModel(getProcessDefinition());
 +        switch (format) {
 +            case JSON:
 +                JsonNode definitionNode;
 +                try {
 +                    definitionNode = new ObjectMapper().readTree(definition);
 +                    if (definitionNode.has(MODEL_DATA_JSON_MODEL)) {
 +                        definitionNode = definitionNode.get(MODEL_DATA_JSON_MODEL);
 +                    }
 +                    if (!definitionNode.has(BpmnJsonConverter.EDITOR_CHILD_SHAPES)) {
 +                        throw new IllegalArgumentException(
 +                                "Could not find JSON node " + BpmnJsonConverter.EDITOR_CHILD_SHAPES);
 +                    }
 +
 +                    BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(definitionNode);
 +                    importUtils.fromXML(new BpmnXMLConverter().convertToXML(bpmnModel));
 +                } catch (Exception e) {
 +                    throw new WorkflowException("While updating process "
 +                            + ActivitiUserWorkflowAdapter.WF_PROCESS_RESOURCE, e);
 +                }
 +
 +                importUtils.fromJSON(definitionNode.toString().getBytes(), getProcessDefinition(), model);
 +                break;
 +
 +            case XML:
 +            default:
 +                importUtils.fromXML(definition.getBytes());
 +
 +                importUtils.fromJSON(getProcessDefinition(), model);
 +        }
 +    }
 +
 +    private WorkflowFormPropertyType fromActivitiFormType(final FormType activitiFormType) {
 +        WorkflowFormPropertyType result = WorkflowFormPropertyType.String;
 +
 +        if ("string".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.String;
 +        }
 +        if ("long".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.Long;
 +        }
 +        if ("enum".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.Enum;
 +        }
 +        if ("date".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.Date;
 +        }
 +        if ("boolean".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.Boolean;
 +        }
 +
 +        return result;
 +    }
 +
 +    private WorkflowFormTO getFormTO(final Task task) {
 +        return getFormTO(task, formService.getTaskFormData(task.getId()));
 +    }
 +
 +    private WorkflowFormTO getFormTO(final Task task, final TaskFormData fd) {
 +        final WorkflowFormTO formTO =
 +                getFormTO(task.getProcessInstanceId(), task.getId(), fd.getFormKey(), fd.getFormProperties());
 +
 +        BeanUtils.copyProperties(task, formTO);
 +        return formTO;
 +    }
 +
 +    private WorkflowFormTO getFormTO(final HistoricTaskInstance task) {
 +        final List<HistoricFormPropertyEntity> props = new ArrayList<>();
 +
 +        for (HistoricDetail historicDetail : historyService.createHistoricDetailQuery().taskId(task.getId()).list()) {
 +
 +            if (historicDetail instanceof HistoricFormPropertyEntity) {
 +                props.add((HistoricFormPropertyEntity) historicDetail);
 +            }
 +        }
 +
 +        final WorkflowFormTO formTO = getHistoricFormTO(
 +                task.getProcessInstanceId(), task.getId(), task.getFormKey(), props);
 +        BeanUtils.copyProperties(task, formTO);
 +
 +        final HistoricActivityInstance historicActivityInstance = historyService.createHistoricActivityInstanceQuery().
 +                executionId(task.getExecutionId()).activityType("userTask").activityName(task.getName()).singleResult();
 +
 +        if (historicActivityInstance != null) {
 +            formTO.setCreateTime(historicActivityInstance.getStartTime());
 +            formTO.setDueDate(historicActivityInstance.getEndTime());
 +        }
 +
 +        return formTO;
 +    }
 +
 +    private WorkflowFormTO getHistoricFormTO(
 +            final String processInstanceId,
 +            final String taskId,
 +            final String formKey,
 +            final List<HistoricFormPropertyEntity> props) {
 +
 +        WorkflowFormTO formTO = new WorkflowFormTO();
 +
 +        User user = userDAO.findByWorkflowId(processInstanceId);
 +        if (user == null) {
 +            throw new NotFoundException("User with workflow id " + processInstanceId);
 +        }
 +        formTO.setUserKey(user.getKey());
 +
 +        formTO.setTaskId(taskId);
 +        formTO.setKey(formKey);
 +
 +        for (HistoricFormPropertyEntity prop : props) {
 +            WorkflowFormPropertyTO propertyTO = new WorkflowFormPropertyTO();
 +            propertyTO.setId(prop.getPropertyId());
 +            propertyTO.setName(prop.getPropertyId());
 +            propertyTO.setValue(prop.getPropertyValue());
 +            formTO.addProperty(propertyTO);
 +        }
 +
 +        return formTO;
 +    }
 +
 +    @SuppressWarnings("unchecked")
 +    private WorkflowFormTO getFormTO(
 +            final String processInstanceId,
 +            final String taskId,
 +            final String formKey,
 +            final List<FormProperty> properties) {
 +
 +        WorkflowFormTO formTO = new WorkflowFormTO();
 +
 +        User user = userDAO.findByWorkflowId(processInstanceId);
 +        if (user == null) {
 +            throw new NotFoundException("User with workflow id " + processInstanceId);
 +        }
 +        formTO.setUserKey(user.getKey());
 +
 +        formTO.setTaskId(taskId);
 +        formTO.setKey(formKey);
 +
 +        for (FormProperty fProp : properties) {
 +            WorkflowFormPropertyTO propertyTO = new WorkflowFormPropertyTO();
 +            BeanUtils.copyProperties(fProp, propertyTO, PROPERTY_IGNORE_PROPS);
 +            propertyTO.setType(fromActivitiFormType(fProp.getType()));
 +
 +            if (propertyTO.getType() == WorkflowFormPropertyType.Date) {
 +                propertyTO.setDatePattern((String) fProp.getType().getInformation("datePattern"));
 +            }
 +            if (propertyTO.getType() == WorkflowFormPropertyType.Enum) {
 +                propertyTO.getEnumValues().putAll((Map<String, String>) fProp.getType().getInformation("values"));
 +            }
 +
 +            formTO.addProperty(propertyTO);
 +        }
 +
 +        return formTO;
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public List<WorkflowFormTO> getForms() {
 +        List<WorkflowFormTO> forms = new ArrayList<>();
 +
 +        final String authUser = AuthContextUtils.getAuthenticatedUsername();
 +        if (adminUser.equals(authUser)) {
 +            forms.addAll(getForms(taskService.createTaskQuery().
 +                    taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE)));
 +        } else {
 +            User user = userDAO.find(authUser);
 +            if (user == null) {
 +                throw new NotFoundException("Syncope User " + authUser);
 +            }
 +
 +            forms.addAll(getForms(taskService.createTaskQuery().
 +                    taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE).
 +                    taskCandidateOrAssigned(user.getKey().toString())));
 +
 +            List<String> candidateGroups = new ArrayList<>();
 +            for (Long groupId : userDAO.findAllGroupKeys(user)) {
 +                candidateGroups.add(groupId.toString());
 +            }
 +            if (!candidateGroups.isEmpty()) {
 +                forms.addAll(getForms(taskService.createTaskQuery().
 +                        taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE).
 +                        taskCandidateGroupIn(candidateGroups)));
 +            }
 +        }
 +
 +        return forms;
 +    }
 +
 +    @Override
 +    public List<WorkflowFormTO> getForms(final String workflowId, final String name) {
 +        List<WorkflowFormTO> forms = getForms(
 +                taskService.createTaskQuery().processInstanceId(workflowId).taskName(name).
 +                taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE));
 +
 +        forms.addAll(getForms(historyService.createHistoricTaskInstanceQuery().taskName(name).
 +                taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE)));
 +
 +        return forms;
 +    }
 +
 +    private <T extends Query<?, ?>, U extends Object> List<WorkflowFormTO> getForms(final Query<T, U> query) {
 +        List<WorkflowFormTO> forms = new ArrayList<>();
 +
 +        for (U obj : query.list()) {
 +            try {
 +                if (obj instanceof HistoricTaskInstance) {
 +                    forms.add(getFormTO((HistoricTaskInstance) obj));
 +                } else if (obj instanceof Task) {
 +                    forms.add(getFormTO((Task) obj));
 +                } else {
 +                    throw new ActivitiException(
 +                            "Failure retrieving form", new IllegalArgumentException("Invalid task type"));
 +                }
 +            } catch (ActivitiException e) {
 +                LOG.debug("No form found for task {}", obj, e);
 +            }
 +        }
 +
 +        return forms;
 +    }
 +
 +    @Override
 +    public WorkflowFormTO getForm(final String workflowId) {
 +        Task task;
 +        try {
 +            task = taskService.createTaskQuery().processInstanceId(workflowId).singleResult();
 +        } catch (ActivitiException e) {
 +            throw new WorkflowException("While reading form for workflow instance " + workflowId, e);
 +        }
 +
 +        TaskFormData formData;
 +        try {
 +            formData = formService.getTaskFormData(task.getId());
 +        } catch (ActivitiException e) {
 +            LOG.debug("No form found for task {}", task.getId(), e);
 +            formData = null;
 +        }
 +
 +        WorkflowFormTO result = null;
 +        if (formData != null && !formData.getFormProperties().isEmpty()) {
 +            result = getFormTO(task);
 +        }
 +
 +        return result;
 +    }
 +
 +    private Pair<Task, TaskFormData> checkTask(final String taskId, final String authUser) {
 +        Task task;
 +        try {
 +            task = taskService.createTaskQuery().taskId(taskId).singleResult();
 +        } catch (ActivitiException e) {
 +            throw new NotFoundException("Activiti Task " + taskId, e);
 +        }
 +
 +        TaskFormData formData;
 +        try {
 +            formData = formService.getTaskFormData(task.getId());
 +        } catch (ActivitiException e) {
 +            throw new NotFoundException("Form for Activiti Task " + taskId, e);
 +        }
 +
 +        if (!adminUser.equals(authUser)) {
 +            User user = userDAO.find(authUser);
 +            if (user == null) {
 +                throw new NotFoundException("Syncope User " + authUser);
 +            }
 +        }
 +
 +        return new ImmutablePair<>(task, formData);
 +    }
 +
 +    @Transactional
 +    @Override
 +    public WorkflowFormTO claimForm(final String taskId) {
 +        final String authUser = AuthContextUtils.getAuthenticatedUsername();
 +        Pair<Task, TaskFormData> checked = checkTask(taskId, authUser);
 +
 +        if (!adminUser.equals(authUser)) {
 +            List<Task> tasksForUser = taskService.createTaskQuery().taskId(taskId).taskCandidateUser(authUser).list();
 +            if (tasksForUser.isEmpty()) {
 +                throw new WorkflowException(
 +                        new IllegalArgumentException(authUser + " is not candidate for task " + taskId));
 +            }
 +        }
 +
 +        Task task;
 +        try {
 +            taskService.setOwner(taskId, authUser);
 +            task = taskService.createTaskQuery().taskId(taskId).singleResult();
 +        } catch (ActivitiException e) {
 +            throw new WorkflowException("While reading task " + taskId, e);
 +        }
 +
 +        return getFormTO(task, checked.getValue());
 +    }
 +
 +    @Transactional
 +    @Override
 +    public WorkflowResult<UserMod> submitForm(final WorkflowFormTO form) {
 +        final String authUser = AuthContextUtils.getAuthenticatedUsername();
 +        Pair<Task, TaskFormData> checked = checkTask(form.getTaskId(), authUser);
 +
 +        if (!checked.getKey().getOwner().equals(authUser)) {
 +            throw new WorkflowException(new IllegalArgumentException("Task " + form.getTaskId() + " assigned to "
 +                    + checked.getKey().getOwner() + " but submitted by " + authUser));
 +        }
 +
 +        User user = userDAO.findByWorkflowId(checked.getKey().getProcessInstanceId());
 +        if (user == null) {
 +            throw new NotFoundException("User with workflow id " + checked.getKey().getProcessInstanceId());
 +        }
 +
 +        Set<String> preTasks = getPerformedTasks(user);
 +        try {
 +            formService.submitTaskFormData(form.getTaskId(), form.getPropertiesForSubmit());
 +            runtimeService.setVariable(user.getWorkflowId(), FORM_SUBMITTER, authUser);
 +        } catch (ActivitiException e) {
 +            throwException(e, "While submitting form for task " + form.getTaskId());
 +        }
 +
 +        Set<String> postTasks = getPerformedTasks(user);
 +        postTasks.removeAll(preTasks);
 +        postTasks.add(form.getTaskId());
 +
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        // see if there is any propagation to be done
 +        PropagationByResource propByRes =
 +                runtimeService.getVariable(user.getWorkflowId(), PROP_BY_RESOURCE, PropagationByResource.class);
 +
 +        // fetch - if available - the encrypted password
 +        String clearPassword = null;
 +        String encryptedPwd = runtimeService.getVariable(user.getWorkflowId(), ENCRYPTED_PWD, String.class);
 +        if (StringUtils.isNotBlank(encryptedPwd)) {
 +            clearPassword = decrypt(encryptedPwd);
 +        }
 +
 +        // supports approval chains
 +        saveForFormSubmit(user, clearPassword, propByRes);
 +
 +        UserMod userMod = runtimeService.getVariable(user.getWorkflowId(), USER_MOD, UserMod.class);
 +        if (userMod == null) {
 +            userMod = new UserMod();
 +            userMod.setKey(updated.getKey());
 +            userMod.setPassword(clearPassword);
 +
 +            if (propByRes != null) {
 +                final StatusMod st = new StatusMod();
 +                userMod.setPwdPropRequest(st);
 +                st.setOnSyncope(true);
 +                for (String res : propByRes.get(ResourceOperation.CREATE)) {
 +                    st.getResourceNames().add(res);
 +                }
 +            }
 +        }
 +
 +        return new WorkflowResult<>(userMod, propByRes, postTasks);
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/fit/console-reference/src/test/java/org/apache/syncope/fit/console/reference/ReportITCase.java
----------------------------------------------------------------------
diff --cc fit/console-reference/src/test/java/org/apache/syncope/fit/console/reference/ReportITCase.java
index 0a6b1b5,0000000..1898c07
mode 100644,000000..100644
--- a/fit/console-reference/src/test/java/org/apache/syncope/fit/console/reference/ReportITCase.java
+++ b/fit/console-reference/src/test/java/org/apache/syncope/fit/console/reference/ReportITCase.java
@@@ -1,117 -1,0 +1,117 @@@
 +/*
 + * 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.console.reference;
 +
 +import static org.junit.Assert.assertTrue;
 +
 +import org.apache.syncope.common.lib.report.StaticReportletConf;
 +import org.junit.Test;
 +import org.openqa.selenium.By;
 +import org.openqa.selenium.support.ui.ExpectedConditions;
 +import org.openqa.selenium.support.ui.Select;
 +
 +public class ReportITCase extends AbstractITCase {
 +
 +    @Test
 +    public void readReportlet() {
 +        seleniumDriver.findElement(By.xpath("//img[@alt=\"Reports\"]")).click();
 +
 +        wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//div[@id='tabs']")));
-         seleniumDriver.findElement(By.xpath("//table/tbody/tr/td[8]/div/span[13]/a")).click();
++        seleniumDriver.findElement(By.xpath("//table/tbody/tr/td[9]/div/span[13]/a")).click();
 +
 +        wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//iframe")));
 +        seleniumDriver.switchTo().frame(0);
 +
 +        wait.until(ExpectedConditions.presenceOfElementLocated(
 +                By.xpath("//div[2]/form/div[2]/div/div/span/div/div/div/span")));
 +
 +        seleniumDriver.findElement(By.xpath("//div[2]/form/div[2]/div/div/span/div/div[5]/div[2]/div/div[2]/div/a"))
 +                .click();
 +        wait.until(ExpectedConditions.presenceOfElementLocated(
 +                By.xpath("//div[2]/form/div[2]/div/div/span/div/div[5]/div[2]/div/div/select")));
 +
 +        Select select = new Select(seleniumDriver.findElement(
 +                By.xpath("//div[2]/form/div[2]/div/div/span/div/div[5]/div[2]/div/div/select")));
 +        select.selectByVisibleText("testUserReportlet");
 +
 +        seleniumDriver.findElement(
 +                By.xpath("//div[2]/form/div[2]/div/div/span/div/div[5]/div[2]/div[2]/div[2]/a")).click();
 +
 +        seleniumDriver.switchTo().defaultContent();
 +        wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(
 +                By.xpath("//div[7]/form/div/div[2]/div/div/div/div[2]/div/div/iframe")));
 +
 +        wait.until(ExpectedConditions.presenceOfElementLocated(By.name("reportletClass:dropDownChoiceField")));
 +
 +        select = new Select(seleniumDriver.findElement(By.name("reportletClass:dropDownChoiceField")));
 +        select.selectByVisibleText(StaticReportletConf.class.getName());
 +
 +        seleniumDriver.findElement(By.xpath("//div[2]/form/div[3]/input")).click();
 +
 +        seleniumDriver.switchTo().defaultContent();
 +
 +        seleniumDriver.findElement(By.xpath("//a[@class='w_close']")).click();
 +    }
 +
 +    @Test
 +    public void executeReport() {
 +        seleniumDriver.findElement(By.xpath("//img[@alt=\"Reports\"]")).click();
 +
 +        wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//div[@id='tabs']")));
 +
-         seleniumDriver.findElement(By.xpath("//table/tbody/tr/td[8]/div/span[6]/a")).click();
++        seleniumDriver.findElement(By.xpath("//table/tbody/tr/td[9]/div/span[6]/a")).click();
 +
 +        wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("feedback")));
 +        assertTrue(seleniumDriver.findElement(
 +                By.tagName("body")).getText().contains("Operation executed successfully"));
 +    }
 +
 +    @Test
 +    public void navigateAudit() {
 +        seleniumDriver.findElement(By.xpath("//img[@alt=\"Reports\"]")).click();
 +        wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//div[@id='tabs']")));
 +
 +        seleniumDriver.findElement(By.xpath("//div[@id='tabs']/ul/li[2]/a/span")).click();
 +
 +        wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath(
 +                "//option[contains(text(),'[REST]:[EntitlementLogic]:[]:[getOwn]:[SUCCESS]')]")));
 +
 +        Select select = new Select(seleniumDriver.findElement(By.xpath(
 +                "//select[@name='events:categoryContainer:type:dropDownChoiceField']")));
 +        select.selectByVisibleText("PROPAGATION");
 +
 +        wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath(
 +                "//select[@name='events:categoryContainer:category:dropDownChoiceField']/option[text()='user']")));
 +
 +        select = new Select(seleniumDriver.findElement(By.xpath(
 +                "//select[@name='events:categoryContainer:category:dropDownChoiceField']")));
 +        select.selectByVisibleText("user");
 +
 +        wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath(
 +                "//select[@name='events:categoryContainer:subcategory:dropDownChoiceField']"
 +                + "/option[text()='resource-csv']")));
 +
 +        select = new Select(seleniumDriver.findElement(By.xpath(
 +                "//select[@name='events:categoryContainer:subcategory:dropDownChoiceField']")));
 +        select.selectByVisibleText("resource-csv");
 +
 +        wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath(
 +                "//input[@name='events:eventsContainer:eventsPanel:successGroup']")));
 +    }
 +}


[7/7] syncope git commit: [SYNCOPE-676] Merge from 1_2_X

Posted by il...@apache.org.
[SYNCOPE-676] Merge from 1_2_X


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

Branch: refs/heads/master
Commit: 97607b16e4ed807ff055b3ae55f0c722717c6472
Parents: 3eb0bfb 2612337
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Tue Jul 7 09:37:35 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Tue Jul 7 09:37:35 2015 +0200

----------------------------------------------------------------------
 .../client/console/commons/Constants.java       |  2 -
 .../client/console/rest/GroupRestClient.java    |  4 +-
 .../client/console/rest/UserRestClient.java     |  4 +-
 .../lib/builders/AnyListQueryBuilder.java       |  6 ++
 .../lib/builders/AnySearchQueryBuilder.java     |  6 ++
 .../client/lib/builders/ListQueryBuilder.java   |  6 ++
 .../pages/DisplayAttributesModalPage.java       | 48 +----------
 .../console/panels/UserSearchResultPanel.java   | 11 +--
 .../client/console/rest/UserRestClient.java     |  4 +-
 .../pages/DisplayAttributesModalPage.html       |  2 -
 .../common/rest/api/beans/ListQuery.java        | 12 +++
 .../common/rest/api/service/JAXRSService.java   |  2 +
 .../syncope/core/logic/AbstractAnyLogic.java    | 10 ++-
 .../syncope/core/logic/AnyObjectLogic.java      | 16 ++--
 .../apache/syncope/core/logic/GroupLogic.java   | 13 +--
 .../apache/syncope/core/logic/UserLogic.java    | 13 +--
 .../core/logic/report/GroupReportlet.java       |  2 +-
 .../core/logic/report/UserReportlet.java        |  7 +-
 .../syncope/core/logic/NotificationTest.java    |  7 +-
 .../src/main/resources/indexes.xml              | 31 ++++++++
 .../api/data/AnyObjectDataBinder.java           |  2 +-
 .../provisioning/api/data/GroupDataBinder.java  |  2 +-
 .../provisioning/api/data/UserDataBinder.java   |  2 +-
 .../java/data/AnyObjectDataBinderImpl.java      | 53 +++++++------
 .../java/data/GroupDataBinderImpl.java          | 10 ++-
 .../java/data/UserDataBinderImpl.java           | 83 +++++++++++---------
 .../notification/NotificationManagerImpl.java   |  6 +-
 .../sync/AnyObjectPushResultHandlerImpl.java    |  4 +-
 .../java/sync/GroupPushResultHandlerImpl.java   |  4 +-
 .../rest/cxf/service/AbstractAnyService.java    |  6 +-
 .../rest/cxf/service/AbstractServiceImpl.java   |  3 -
 .../rest/cxf/service/AnyObjectServiceImpl.java  |  3 +-
 .../activiti/ActivitiUserWorkflowAdapter.java   |  4 +-
 .../fit/console/reference/ReportITCase.java     |  4 +-
 34 files changed, 212 insertions(+), 180 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
----------------------------------------------------------------------
diff --cc client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
index 0b8391a,0000000..98339ec
mode 100644,000000..100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
@@@ -1,110 -1,0 +1,108 @@@
 +/*
 + * 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.client.console.commons;
 +
 +public final class Constants {
 +
 +    public static final String ON_CLICK = "onclick";
 +
 +    public static final String ON_CHANGE = "onchange";
 +
 +    public static final String ON_BLUR = "onblur";
 +
 +    public static final String PNG_EXT = ".png";
 +
 +    public static final String FEEDBACK = "feedback";
 +
 +    public static final String OPERATION_SUCCEEDED = "operation_succeeded";
 +
 +    public static final String OPERATION_ERROR = "operation_error";
 +
 +    public static final String SEARCH_ERROR = "search_error";
 +
 +    public static final String ERROR = "error";
 +
 +    public static final String PARAM_PASSWORD_RESET_TOKEN = "pwdResetToken";
 +
 +    public static final String PREF_USERS_DETAILS_VIEW = "users.details.view";
 +
 +    public static final String PREF_USERS_ATTRIBUTES_VIEW = "users.attributes.view";
 +
 +    public static final String PREF_USERS_DERIVED_ATTRIBUTES_VIEW = "users.derived.attributes.view";
 +
-     public static final String PREF_USERS_VIRTUAL_ATTRIBUTES_VIEW = "users.virtual.attributes.view";
- 
 +    public static final String PREF_CONF_SCHEMA_PAGINATOR_ROWS = "conf.schema.paginator.rows";
 +
 +    public static final String PREF_USER_PLAIN_SCHEMA_PAGINATOR_ROWS = "user.schema.paginator.rows";
 +
 +    public static final String PREF_USER_DER_SCHEMA_PAGINATOR_ROWS = "user.derived.schema.paginator.rows";
 +
 +    public static final String PREF_USER_VIR_SCHEMA_PAGINATOR_ROWS = "user.virtual.schema.paginator.rows";
 +
 +    public static final String PREF_GROUP_PLAIN_SCHEMA_PAGINATOR_ROWS = "group.schema.paginator.rows";
 +
 +    public static final String PREF_GROUP_DER_SCHEMA_PAGINATOR_ROWS = "group.derived.schema.paginator.rows";
 +
 +    public static final String PREF_GROUP_VIR_SCHEMA_PAGINATOR_ROWS = "group.virtual.schema.paginator.rows";
 +
 +    public static final String PREF_MEMBERSHIP_PLAIN_SCHEMA_PAGINATOR_ROWS = "membership.schema.paginator.rows";
 +
 +    public static final String PREF_MEMBERSHIP_DER_SCHEMA_PAGINATOR_ROWS = "membership.derived.aschema.paginator.rows";
 +
 +    public static final String PREF_MEMBERSHIP_VIR_SCHEMA_PAGINATOR_ROWS = "membership.virtual.aschema.paginator.rows";
 +
 +    public static final String PREF_USERS_PAGINATOR_ROWS = "users.paginator.rows";
 +
 +    public static final String PREF_RESOURCES_PAGINATOR_ROWS = "resources.paginator.rows";
 +
 +    public static final String PREF_CONNECTORS_PAGINATOR_ROWS = "connectors.paginator.rows";
 +
 +    public static final String PREF_NOTIFICATION_PAGINATOR_ROWS = "notification.paginator.rows";
 +
 +    public static final String PREF_PROPAGATION_TASKS_PAGINATOR_ROWS = "proagationtasks.paginator.rows";
 +
 +    public static final String PREF_NOTIFICATION_TASKS_PAGINATOR_ROWS = "notificationtasks.paginator.rows";
 +
 +    public static final String PREF_SCHED_TASKS_PAGINATOR_ROWS = "schedtasks.paginator.rows";
 +
 +    public static final String PREF_SYNC_TASKS_PAGINATOR_ROWS = "synctasks.paginator.rows";
 +
 +    public static final String PREF_TODO_PAGINATOR_ROWS = "todo.paginator.rows";
 +
 +    public static final String PREF_REPORT_PAGINATOR_ROWS = "report.paginator.rows";
 +
 +    public static final String PAGEPARAM_CREATE = "CREATE";
 +
 +    public static final String PAGEPARAM_CURRENT_PAGE = "_current_page";
 +
 +    public static final String PREF_POLICY_PAGINATOR_ROWS = "policy.paginator.rows";
 +
 +    /**
 +     * ConnId's GuardedString is not in the classpath.
 +     */
 +    public static final String GUARDED_STRING = "org.identityconnectors.common.security.GuardedString";
 +
 +    /**
 +     * ConnId's GuardedByteArray is not in the classpath.
 +     */
 +    public static final String GUARDED_BYTE_ARRAY = "org.identityconnectors.common.security.GuardedByteArray";
 +
 +    private Constants() {
 +        // private constructor for static utility class
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/client/console/src/main/java/org/apache/syncope/client/console/rest/GroupRestClient.java
----------------------------------------------------------------------
diff --cc client/console/src/main/java/org/apache/syncope/client/console/rest/GroupRestClient.java
index b4b848c,0000000..512e08f
mode 100644,000000..100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/rest/GroupRestClient.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/GroupRestClient.java
@@@ -1,204 -1,0 +1,204 @@@
 +/*
 + * 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.client.console.rest;
 +
 +import java.util.List;
 +
 +import javax.ws.rs.core.Response;
 +import org.apache.syncope.client.console.commons.status.StatusBean;
 +import org.apache.syncope.client.console.commons.status.StatusUtils;
 +import org.apache.syncope.client.lib.SyncopeClient;
 +import org.apache.syncope.common.lib.mod.GroupMod;
 +import org.apache.syncope.common.lib.mod.ResourceAssociationMod;
 +import org.apache.syncope.common.lib.to.BulkAction;
 +import org.apache.syncope.common.lib.to.BulkActionResult;
 +import org.apache.syncope.common.lib.to.ConnObjectTO;
 +import org.apache.syncope.common.lib.to.GroupTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.ResourceAssociationActionType;
 +import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
 +import org.apache.syncope.common.lib.wrap.ResourceKey;
 +import org.apache.syncope.common.rest.api.CollectionWrapper;
 +import org.apache.syncope.common.rest.api.service.ResourceService;
 +import org.apache.syncope.common.rest.api.service.GroupService;
 +import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 +import org.springframework.stereotype.Component;
 +
 +/**
 + * Console client for invoking Rest Group's services.
 + */
 +@Component
 +public class GroupRestClient extends AbstractAnyRestClient {
 +
 +    private static final long serialVersionUID = -8549081557283519638L;
 +
 +    @Override
 +    public int count(final String realm) {
 +        return getService(GroupService.class).
 +                list(SyncopeClient.getAnyListQueryBuilder().realm(realm).page(1).size(1).build()).
 +                getTotalCount();
 +    }
 +
 +    @Override
 +    public List<GroupTO> list(final String realm, final int page, final int size, final SortParam<String> sort) {
 +        return getService(GroupService.class).
 +                list(SyncopeClient.getAnyListQueryBuilder().realm(realm).page(page).size(size).
-                         orderBy(toOrderBy(sort)).build()).
++                        orderBy(toOrderBy(sort)).details(false).build()).
 +                getResult();
 +    }
 +
 +    @Override
 +    public int searchCount(final String realm, final String fiql) {
 +        return getService(GroupService.class).
 +                search(SyncopeClient.getAnySearchQueryBuilder().realm(realm).fiql(fiql).page(1).size(1).build()).
 +                getTotalCount();
 +    }
 +
 +    @Override
 +    public List<GroupTO> search(
 +            final String realm, final String fiql, final int page, final int size, final SortParam<String> sort) {
 +
 +        return getService(GroupService.class).
 +                search(SyncopeClient.getAnySearchQueryBuilder().realm(realm).fiql(fiql).page(page).size(size).
-                         orderBy(toOrderBy(sort)).build()).
++                        orderBy(toOrderBy(sort)).details(false).build()).
 +                getResult();
 +    }
 +
 +    @Override
 +    public ConnObjectTO readConnObject(final String resourceName, final Long id) {
 +        return getService(ResourceService.class).readConnObject(resourceName, AnyTypeKind.GROUP.name(), id);
 +    }
 +
 +    public GroupTO create(final GroupTO groupTO) {
 +        Response response = getService(GroupService.class).create(groupTO);
 +        return response.readEntity(GroupTO.class);
 +    }
 +
 +    public GroupTO read(final Long key) {
 +        return getService(GroupService.class).read(key);
 +    }
 +
 +    public GroupTO update(final String etag, final GroupMod groupMod) {
 +        GroupTO result;
 +        synchronized (this) {
 +            GroupService service = getService(etag, GroupService.class);
 +            result = service.update(groupMod).readEntity(GroupTO.class);
 +            resetClient(GroupService.class);
 +        }
 +        return result;
 +    }
 +
 +    @Override
 +    public GroupTO delete(final String etag, final Long key) {
 +        GroupTO result;
 +        synchronized (this) {
 +            GroupService service = getService(etag, GroupService.class);
 +            result = service.delete(key).readEntity(GroupTO.class);
 +            resetClient(GroupService.class);
 +        }
 +        return result;
 +    }
 +
 +    @Override
 +    public BulkActionResult bulkAction(final BulkAction action) {
 +        return getService(GroupService.class).bulk(action);
 +    }
 +
 +    public void unlink(final String etag, final long groupKey, final List<StatusBean> statuses) {
 +        synchronized (this) {
 +            GroupService service = getService(etag, GroupService.class);
 +            service.bulkDeassociation(groupKey, ResourceDeassociationActionType.UNLINK,
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
 +                            ResourceKey.class));
 +            resetClient(GroupService.class);
 +        }
 +    }
 +
 +    public void link(final String etag, final long groupKey, final List<StatusBean> statuses) {
 +        synchronized (this) {
 +            GroupService service = getService(etag, GroupService.class);
 +
 +            ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +            associationMod.getTargetResources().addAll(
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(), ResourceKey.class));
 +            service.bulkAssociation(groupKey, ResourceAssociationActionType.LINK, associationMod);
 +
 +            resetClient(GroupService.class);
 +        }
 +    }
 +
 +    public BulkActionResult deprovision(final String etag, final long groupKey, final List<StatusBean> statuses) {
 +        BulkActionResult result;
 +        synchronized (this) {
 +            GroupService service = getService(etag, GroupService.class);
 +            result = service.bulkDeassociation(groupKey, ResourceDeassociationActionType.DEPROVISION,
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
 +                            ResourceKey.class)).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(GroupService.class);
 +        }
 +        return result;
 +    }
 +
 +    public BulkActionResult provision(final String etag, final long groupKey, final List<StatusBean> statuses) {
 +        BulkActionResult result;
 +        synchronized (this) {
 +            GroupService service = getService(etag, GroupService.class);
 +
 +            ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +            associationMod.getTargetResources().addAll(
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(), ResourceKey.class));
 +
 +            result = service.bulkAssociation(groupKey, ResourceAssociationActionType.PROVISION, associationMod).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(GroupService.class);
 +        }
 +        return result;
 +    }
 +
 +    public BulkActionResult unassign(final String etag, final long groupKey, final List<StatusBean> statuses) {
 +        BulkActionResult result;
 +        synchronized (this) {
 +            GroupService service = getService(etag, GroupService.class);
 +            result = service.bulkDeassociation(groupKey, ResourceDeassociationActionType.UNASSIGN,
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
 +                            ResourceKey.class)).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(GroupService.class);
 +        }
 +        return result;
 +    }
 +
 +    public BulkActionResult assign(final String etag, final long groupKey, final List<StatusBean> statuses) {
 +        BulkActionResult result;
 +        synchronized (this) {
 +            GroupService service = getService(etag, GroupService.class);
 +
 +            ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +            associationMod.getTargetResources().addAll(
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(), ResourceKey.class));
 +
 +            result = service.bulkAssociation(groupKey, ResourceAssociationActionType.ASSIGN, associationMod).
 +                    readEntity(BulkActionResult.class);
 +
 +            resetClient(GroupService.class);
 +        }
 +        return result;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/client/console/src/main/java/org/apache/syncope/client/console/rest/UserRestClient.java
----------------------------------------------------------------------
diff --cc client/console/src/main/java/org/apache/syncope/client/console/rest/UserRestClient.java
index f517c4c,0000000..338851d
mode 100644,000000..100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/rest/UserRestClient.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/UserRestClient.java
@@@ -1,238 -1,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.client.console.rest;
 +
 +import java.util.List;
 +import javax.ws.rs.core.Response;
 +import org.apache.syncope.client.console.commons.status.StatusBean;
 +import org.apache.syncope.client.console.commons.status.StatusUtils;
 +import org.apache.syncope.client.lib.SyncopeClient;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +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.BulkAction;
 +import org.apache.syncope.common.lib.to.BulkActionResult;
 +import org.apache.syncope.common.lib.to.ConnObjectTO;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.ResourceAssociationActionType;
 +import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
 +import org.apache.syncope.common.lib.wrap.ResourceKey;
 +import org.apache.syncope.common.rest.api.CollectionWrapper;
 +import org.apache.syncope.common.rest.api.service.ResourceService;
 +import org.apache.syncope.common.rest.api.service.UserService;
 +import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 +import org.springframework.stereotype.Component;
 +
 +/**
 + * Console client for invoking rest users services.
 + */
 +@Component
 +public class UserRestClient extends AbstractAnyRestClient {
 +
 +    private static final long serialVersionUID = -1575748964398293968L;
 +
 +    @Override
 +    public int count(final String realm) {
 +        return getService(UserService.class).
 +                list(SyncopeClient.getAnyListQueryBuilder().realm(realm).page(1).size(1).build()).
 +                getTotalCount();
 +    }
 +
 +    @Override
 +    public List<UserTO> list(final String realm, final int page, final int size, final SortParam<String> sort) {
 +        return getService(UserService.class).
 +                list(SyncopeClient.getAnyListQueryBuilder().realm(realm).page(page).size(size).
-                         orderBy(toOrderBy(sort)).build()).
++                        orderBy(toOrderBy(sort)).details(false).build()).
 +                getResult();
 +    }
 +
 +    public UserTO create(final UserTO userTO, final boolean storePassword) {
 +        Response response = getService(UserService.class).create(userTO, storePassword);
 +        return response.readEntity(UserTO.class);
 +    }
 +
 +    public UserTO update(final String etag, final UserMod userMod) {
 +        UserTO result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            result = service.update(userMod).readEntity(UserTO.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +
 +    @Override
 +    public UserTO delete(final String etag, final Long id) {
 +        UserTO result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            result = service.delete(id).readEntity(UserTO.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +
 +    public UserTO read(final Long id) {
 +        UserTO userTO = null;
 +        try {
 +            userTO = getService(UserService.class).read(id);
 +        } catch (SyncopeClientException e) {
 +            LOG.error("While reading a user", e);
 +        }
 +        return userTO;
 +    }
 +
 +    @Override
 +    public int searchCount(final String realm, final String fiql) {
 +        return getService(UserService.class).
 +                search(SyncopeClient.getAnySearchQueryBuilder().realm(realm).fiql(fiql).page(1).size(1).build()).
 +                getTotalCount();
 +    }
 +
 +    @Override
 +    public List<UserTO> search(
 +            final String realm, final String fiql, final int page, final int size, final SortParam<String> sort) {
 +
 +        return getService(UserService.class).
 +                search(SyncopeClient.getAnySearchQueryBuilder().realm(realm).fiql(fiql).page(page).size(size).
-                         orderBy(toOrderBy(sort)).build()).
++                        orderBy(toOrderBy(sort)).details(false).build()).
 +                getResult();
 +    }
 +
 +    @Override
 +    public ConnObjectTO readConnObject(final String resourceName, final Long id) {
 +        return getService(ResourceService.class).readConnObject(resourceName, AnyTypeKind.USER.name(), id);
 +    }
 +
 +    public void suspend(final String etag, final long userKey, final List<StatusBean> statuses) {
 +        StatusMod statusMod = StatusUtils.buildStatusMod(statuses, false);
 +        statusMod.setType(StatusMod.ModType.SUSPEND);
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            service.status(userKey, statusMod);
 +            resetClient(UserService.class);
 +        }
 +    }
 +
 +    public void reactivate(final String etag, final long userKey, final List<StatusBean> statuses) {
 +        StatusMod statusMod = StatusUtils.buildStatusMod(statuses, true);
 +        statusMod.setType(StatusMod.ModType.REACTIVATE);
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            service.status(userKey, statusMod);
 +            resetClient(UserService.class);
 +        }
 +    }
 +
 +    @Override
 +    public BulkActionResult bulkAction(final BulkAction action) {
 +        return getService(UserService.class).bulk(action);
 +    }
 +
 +    public void unlink(final String etag, final long userKey, final List<StatusBean> statuses) {
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            service.bulkDeassociation(userKey, ResourceDeassociationActionType.UNLINK,
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
 +                            ResourceKey.class));
 +            resetClient(UserService.class);
 +        }
 +    }
 +
 +    public void link(final String etag, final long userKey, final List<StatusBean> statuses) {
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +
 +            ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +            associationMod.getTargetResources().addAll(
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(), ResourceKey.class));
 +            service.bulkAssociation(userKey, ResourceAssociationActionType.LINK, associationMod);
 +
 +            resetClient(UserService.class);
 +        }
 +    }
 +
 +    public BulkActionResult deprovision(final String etag, final long userKey, final List<StatusBean> statuses) {
 +        BulkActionResult result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            result = service.bulkDeassociation(userKey, ResourceDeassociationActionType.DEPROVISION,
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
 +                            ResourceKey.class)).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +
 +    public BulkActionResult provision(final String etag, final long userKey,
 +            final List<StatusBean> statuses, final boolean changepwd, final String password) {
 +
 +        BulkActionResult result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +
 +            ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +            associationMod.getTargetResources().addAll(
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(), ResourceKey.class));
 +            associationMod.setChangePwd(changepwd);
 +            associationMod.setPassword(password);
 +
 +            result = service.bulkAssociation(userKey, ResourceAssociationActionType.PROVISION, associationMod).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +
 +    public BulkActionResult unassign(final String etag, final long userKey, final List<StatusBean> statuses) {
 +        BulkActionResult result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            result = service.bulkDeassociation(userKey, ResourceDeassociationActionType.UNASSIGN,
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
 +                            ResourceKey.class)).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +
 +    public BulkActionResult assign(final String etag, final long userKey,
 +            final List<StatusBean> statuses, final boolean changepwd, final String password) {
 +
 +        BulkActionResult result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +
 +            ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +            associationMod.getTargetResources().addAll(
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(), ResourceKey.class));
 +            associationMod.setChangePwd(changepwd);
 +            associationMod.setPassword(password);
 +
 +            result = service.bulkAssociation(userKey, ResourceAssociationActionType.ASSIGN, associationMod).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/client/lib/src/main/java/org/apache/syncope/client/lib/builders/AnyListQueryBuilder.java
----------------------------------------------------------------------
diff --cc client/lib/src/main/java/org/apache/syncope/client/lib/builders/AnyListQueryBuilder.java
index afd29e9,0000000..e25daef
mode 100644,000000..100644
--- a/client/lib/src/main/java/org/apache/syncope/client/lib/builders/AnyListQueryBuilder.java
+++ b/client/lib/src/main/java/org/apache/syncope/client/lib/builders/AnyListQueryBuilder.java
@@@ -1,62 -1,0 +1,68 @@@
 +/*
 + * 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.client.lib.builders;
 +
 +import java.util.ArrayList;
 +import org.apache.syncope.common.rest.api.beans.ListQuery;
 +import org.apache.syncope.common.rest.api.beans.AnyListQuery;
 +
 +public class AnyListQueryBuilder extends ListQueryBuilder {
 +
 +    private final AnyListQuery instance = new AnyListQuery();
 +
 +    @Override
 +    public AnyListQueryBuilder page(final Integer page) {
 +        return AnyListQueryBuilder.class.cast(super.page(page));
 +    }
 +
 +    @Override
 +    public AnyListQueryBuilder size(final Integer size) {
 +        return AnyListQueryBuilder.class.cast(super.size(size));
 +    }
 +
 +    @Override
 +    public AnyListQueryBuilder orderBy(final String orderBy) {
 +        return AnyListQueryBuilder.class.cast(super.orderBy(orderBy));
 +    }
 +
++    @Override
++    public AnyListQueryBuilder details(final boolean details) {
++        return AnyListQueryBuilder.class.cast(super.details(details));
++    }
++
 +    public AnyListQueryBuilder realm(final String realm) {
 +        if (instance.getRealms() == null) {
 +            instance.setRealms(new ArrayList<String>());
 +        }
 +        instance.getRealms().add(realm);
 +
 +        return this;
 +    }
 +
 +    @Override
 +    public AnyListQuery build() {
 +        ListQuery lq = super.build();
 +        instance.setPage(lq.getPage());
 +        instance.setSize(lq.getSize());
 +        instance.setOrderBy(lq.getOrderBy());
++        instance.setDetails(lq.isDetails());
 +
 +        return instance;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/client/lib/src/main/java/org/apache/syncope/client/lib/builders/AnySearchQueryBuilder.java
----------------------------------------------------------------------
diff --cc client/lib/src/main/java/org/apache/syncope/client/lib/builders/AnySearchQueryBuilder.java
index c3fce47,0000000..5bffdcc
mode 100644,000000..100644
--- a/client/lib/src/main/java/org/apache/syncope/client/lib/builders/AnySearchQueryBuilder.java
+++ b/client/lib/src/main/java/org/apache/syncope/client/lib/builders/AnySearchQueryBuilder.java
@@@ -1,64 -1,0 +1,70 @@@
 +/*
 + * 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.client.lib.builders;
 +
 +import org.apache.syncope.common.rest.api.beans.AnyListQuery;
 +import org.apache.syncope.common.rest.api.beans.AnySearchQuery;
 +
 +public class AnySearchQueryBuilder extends AnyListQueryBuilder {
 +
 +    private final AnySearchQuery instance = new AnySearchQuery();
 +
 +    @Override
 +    public AnySearchQueryBuilder realm(final String realm) {
 +        return AnySearchQueryBuilder.class.cast(super.realm(realm));
 +    }
 +
 +    @Override
 +    public AnySearchQueryBuilder page(final Integer page) {
 +        return AnySearchQueryBuilder.class.cast(super.page(page));
 +    }
 +
 +    @Override
 +    public AnySearchQueryBuilder size(final Integer size) {
 +        return AnySearchQueryBuilder.class.cast(super.size(size));
 +    }
 +
 +    @Override
 +    public AnySearchQueryBuilder orderBy(final String orderBy) {
 +        return AnySearchQueryBuilder.class.cast(super.orderBy(orderBy));
 +    }
 +
++    @Override
++    public AnySearchQueryBuilder details(final boolean details) {
++        return AnySearchQueryBuilder.class.cast(super.details(details));
++    }
++
 +    public AnySearchQueryBuilder fiql(final String fiql) {
 +        instance.setFiql(fiql);
 +
 +        return this;
 +    }
 +
 +    @Override
 +    public AnySearchQuery build() {
 +        AnyListQuery slq = super.build();
 +        instance.setRealms(slq.getRealms());
 +        instance.setPage(slq.getPage());
 +        instance.setSize(slq.getSize());
 +        instance.setOrderBy(slq.getOrderBy());
++        instance.setDetails(slq.isDetails());
 +
 +        return instance;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/client/lib/src/main/java/org/apache/syncope/client/lib/builders/ListQueryBuilder.java
----------------------------------------------------------------------
diff --cc client/lib/src/main/java/org/apache/syncope/client/lib/builders/ListQueryBuilder.java
index 5d6202a,0000000..bce0e73
mode 100644,000000..100644
--- a/client/lib/src/main/java/org/apache/syncope/client/lib/builders/ListQueryBuilder.java
+++ b/client/lib/src/main/java/org/apache/syncope/client/lib/builders/ListQueryBuilder.java
@@@ -1,48 -1,0 +1,54 @@@
 +/*
 + * 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.client.lib.builders;
 +
 +import org.apache.syncope.common.rest.api.beans.ListQuery;
 +
 +public class ListQueryBuilder {
 +
 +    private final ListQuery instance = new ListQuery();
 +
 +    public ListQueryBuilder page(final Integer page) {
 +        instance.setPage(page);
 +
 +        return this;
 +    }
 +
 +    public ListQueryBuilder size(final Integer size) {
 +        instance.setSize(size);
 +
 +        return this;
 +    }
 +
 +    public ListQueryBuilder orderBy(final String orderBy) {
 +        instance.setOrderBy(orderBy);
 +
 +        return this;
 +    }
 +
++    public ListQueryBuilder details(final boolean details) {
++        instance.setDetails(details);
++
++        return this;
++    }
++
 +    public ListQuery build() {
 +        return instance;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/client/old_console/src/main/java/org/apache/syncope/client/console/pages/DisplayAttributesModalPage.java
----------------------------------------------------------------------
diff --cc client/old_console/src/main/java/org/apache/syncope/client/console/pages/DisplayAttributesModalPage.java
index 6f89ec3,0000000..d588a01
mode 100644,000000..100644
--- a/client/old_console/src/main/java/org/apache/syncope/client/console/pages/DisplayAttributesModalPage.java
+++ b/client/old_console/src/main/java/org/apache/syncope/client/console/pages/DisplayAttributesModalPage.java
@@@ -1,273 -1,0 +1,231 @@@
 +/*
 + * 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.client.console.pages;
 +
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +import org.apache.syncope.client.console.commons.Constants;
 +import org.apache.syncope.client.console.commons.PreferenceManager;
 +import org.apache.syncope.common.lib.search.SearchableFields;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.wicket.PageReference;
 +import org.apache.wicket.ajax.AjaxRequestTarget;
 +import org.apache.wicket.ajax.markup.html.form.AjaxButton;
 +import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
 +import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
 +import org.apache.wicket.markup.html.basic.Label;
 +import org.apache.wicket.markup.html.form.Check;
 +import org.apache.wicket.markup.html.form.CheckGroup;
 +import org.apache.wicket.markup.html.form.Form;
 +import org.apache.wicket.markup.html.list.ListItem;
 +import org.apache.wicket.markup.html.list.ListView;
 +import org.apache.wicket.markup.html.panel.Fragment;
 +import org.apache.wicket.model.CompoundPropertyModel;
 +import org.apache.wicket.model.IModel;
 +import org.apache.wicket.model.LoadableDetachableModel;
 +import org.apache.wicket.model.PropertyModel;
 +import org.apache.wicket.model.ResourceModel;
 +import org.apache.wicket.spring.injection.annot.SpringBean;
 +
 +/**
 + * Modal window with Display attributes form.
 + */
 +@SuppressWarnings({ "unchecked", "rawtypes" })
 +public class DisplayAttributesModalPage extends BaseModalPage {
 +
 +    private static final long serialVersionUID = -4274117450918385110L;
 +
 +    /**
 +     * Max allowed selections.
 +     */
 +    private static final int MAX_SELECTIONS = 9;
 +
 +    public static final String[] DEFAULT_SELECTION = { "key", "username", "status" };
 +
 +    @SpringBean
 +    private PreferenceManager prefMan;
 +
 +    private final List<String> selectedDetails;
 +
 +    private final List<String> selectedPlainSchemas;
 +
 +    private final List<String> selectedDerSchemas;
 +
-     private final List<String> selectedVirSchemas;
- 
 +    public DisplayAttributesModalPage(final PageReference pageRef, final ModalWindow window,
-             final List<String> schemaNames, final List<String> dSchemaNames, final List<String> vSchemaNames) {
++            final List<String> schemaNames, final List<String> dSchemaNames) {
 +
 +        super();
 +
 +        final IModel<List<String>> fnames = new LoadableDetachableModel<List<String>>() {
 +
 +            private static final long serialVersionUID = 5275935387613157437L;
 +
 +            @Override
 +            protected List<String> load() {
 +                return SearchableFields.get(UserTO.class);
 +            }
 +        };
 +
 +        final IModel<List<String>> names = new LoadableDetachableModel<List<String>>() {
 +
 +            private static final long serialVersionUID = 5275935387613157437L;
 +
 +            @Override
 +            protected List<String> load() {
 +                return schemaNames;
 +            }
 +        };
 +
 +        final IModel<List<String>> dsnames = new LoadableDetachableModel<List<String>>() {
 +
 +            private static final long serialVersionUID = 5275935387613157437L;
 +
 +            @Override
 +            protected List<String> load() {
 +                return dSchemaNames;
 +            }
 +        };
 +
-         final IModel<List<String>> vsnames = new LoadableDetachableModel<List<String>>() {
- 
-             private static final long serialVersionUID = 5275935387613157437L;
- 
-             @Override
-             protected List<String> load() {
-                 return vSchemaNames;
-             }
-         };
- 
 +        final Form form = new Form(FORM);
 +        form.setModel(new CompoundPropertyModel(this));
 +
 +        selectedDetails = prefMan.getList(getRequest(), Constants.PREF_USERS_DETAILS_VIEW);
 +
 +        selectedPlainSchemas = prefMan.getList(getRequest(), Constants.PREF_USERS_ATTRIBUTES_VIEW);
 +
 +        selectedDerSchemas = prefMan.getList(getRequest(), Constants.PREF_USERS_DERIVED_ATTRIBUTES_VIEW);
 +
-         selectedVirSchemas = prefMan.getList(getRequest(), Constants.PREF_USERS_VIRTUAL_ATTRIBUTES_VIEW);
- 
 +        final CheckGroup dgroup = new CheckGroup("dCheckGroup", new PropertyModel(this, "selectedDetails"));
 +        form.add(dgroup);
 +
 +        final ListView<String> details = new ListView<String>("details", fnames) {
 +
 +            private static final long serialVersionUID = 9101744072914090143L;
 +
 +            @Override
 +            protected void populateItem(final ListItem<String> item) {
 +                item.add(new Check("dcheck", item.getModel()));
 +                item.add(new Label("dname", new ResourceModel(item.getModelObject(), item.getModelObject())));
 +            }
 +        };
 +        dgroup.add(details);
 +
 +        if (names.getObject() == null || names.getObject().isEmpty()) {
 +            final Fragment fragment = new Fragment("plainSchemas", "emptyFragment", form);
 +            form.add(fragment);
 +
 +            selectedPlainSchemas.clear();
 +        } else {
 +            final Fragment fragment = new Fragment("plainSchemas", "sfragment", form);
 +            form.add(fragment);
 +
 +            final CheckGroup sgroup = new CheckGroup("psCheckGroup", new PropertyModel(this, "selectedPlainSchemas"));
 +            fragment.add(sgroup);
 +
 +            final ListView<String> schemas = new ListView<String>("plainSchemas", names) {
 +
 +                private static final long serialVersionUID = 9101744072914090143L;
 +
 +                @Override
 +                protected void populateItem(final ListItem<String> item) {
 +                    item.add(new Check("scheck", item.getModel()));
 +                    item.add(new Label("sname", new ResourceModel(item.getModelObject(), item.getModelObject())));
 +                }
 +            };
 +            sgroup.add(schemas);
 +        }
 +
 +        if (dsnames.getObject() == null || dsnames.getObject().isEmpty()) {
 +            final Fragment fragment = new Fragment("dschemas", "emptyFragment", form);
 +            form.add(fragment);
 +
 +            selectedDerSchemas.clear();
 +        } else {
 +            final Fragment fragment = new Fragment("dschemas", "dsfragment", form);
 +            form.add(fragment);
 +
 +            final CheckGroup dsgroup = new CheckGroup("dsCheckGroup", new PropertyModel(this, "selectedDerSchemas"));
 +            fragment.add(dsgroup);
 +
 +            final ListView<String> derSchemas = new ListView<String>("derSchemas", dsnames) {
 +
 +                private static final long serialVersionUID = 9101744072914090143L;
 +
 +                @Override
 +                protected void populateItem(ListItem<String> item) {
 +                    item.add(new Check("dscheck", item.getModel()));
 +                    item.add(new Label("dsname", new ResourceModel(item.getModelObject(), item.getModelObject())));
 +                }
 +            };
 +            dsgroup.add(derSchemas);
 +        }
 +
-         if (vsnames.getObject() == null || vsnames.getObject().isEmpty()) {
-             final Fragment fragment = new Fragment("vschemas", "emptyFragment", form);
-             form.add(fragment);
- 
-             selectedVirSchemas.clear();
-         } else {
-             final Fragment fragment = new Fragment("vschemas", "vsfragment", form);
-             form.add(fragment);
- 
-             final CheckGroup vsgroup = new CheckGroup("vsCheckGroup", new PropertyModel(this, "selectedVirSchemas"));
-             fragment.add(vsgroup);
- 
-             final ListView<String> virSchemas = new ListView<String>("virSchemas", vsnames) {
- 
-                 private static final long serialVersionUID = 9101744072914090143L;
- 
-                 @Override
-                 protected void populateItem(ListItem<String> item) {
-                     item.add(new Check("vscheck", item.getModel()));
-                     item.add(new Label("vsname", new ResourceModel(item.getModelObject(), item.getModelObject())));
-                 }
-             };
-             vsgroup.add(virSchemas);
-         }
- 
 +        final AjaxButton submit = new IndicatingAjaxButton(SUBMIT, new ResourceModel(SUBMIT)) {
 +
 +            private static final long serialVersionUID = -4804368561204623354L;
 +
 +            @Override
 +            protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
-                 if (selectedDetails.size() + selectedPlainSchemas.size() + selectedVirSchemas.size() + selectedDerSchemas.
-                         size()
++                if (selectedDetails.size() + selectedPlainSchemas.size() + selectedDerSchemas.size()
 +                        > MAX_SELECTIONS) {
 +
 +                    error(getString("tooManySelections"));
 +                    onError(target, form);
 +                } else {
-                     final Map<String, List<String>> prefs = new HashMap<String, List<String>>();
++                    final Map<String, List<String>> prefs = new HashMap<>();
 +
 +                    prefs.put(Constants.PREF_USERS_DETAILS_VIEW, selectedDetails);
 +
 +                    prefs.put(Constants.PREF_USERS_ATTRIBUTES_VIEW, selectedPlainSchemas);
 +
 +                    prefs.put(Constants.PREF_USERS_DERIVED_ATTRIBUTES_VIEW, selectedDerSchemas);
 +
-                     prefs.put(Constants.PREF_USERS_VIRTUAL_ATTRIBUTES_VIEW, selectedVirSchemas);
- 
 +                    prefMan.setList(getRequest(), getResponse(), prefs);
 +
 +                    ((BasePage) pageRef.getPage()).setModalResult(true);
 +
 +                    window.close(target);
 +                }
 +            }
 +
 +            @Override
 +            protected void onError(final AjaxRequestTarget target, final Form<?> form) {
 +                feedbackPanel.refresh(target);
 +            }
 +        };
 +
 +        form.add(submit);
 +
 +        final AjaxButton cancel = new IndicatingAjaxButton(CANCEL, new ResourceModel(CANCEL)) {
 +
 +            private static final long serialVersionUID = -958724007591692537L;
 +
 +            @Override
 +            protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
 +                window.close(target);
 +            }
 +        };
 +
 +        cancel.setDefaultFormProcessing(false);
 +        form.add(cancel);
 +
 +        add(form);
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/client/old_console/src/main/java/org/apache/syncope/client/console/panels/UserSearchResultPanel.java
----------------------------------------------------------------------
diff --cc client/old_console/src/main/java/org/apache/syncope/client/console/panels/UserSearchResultPanel.java
index 132bb66,0000000..16f7a22
mode 100644,000000..100644
--- a/client/old_console/src/main/java/org/apache/syncope/client/console/panels/UserSearchResultPanel.java
+++ b/client/old_console/src/main/java/org/apache/syncope/client/console/panels/UserSearchResultPanel.java
@@@ -1,297 -1,0 +1,288 @@@
 +/*
 + * 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.client.console.panels;
 +
 +import java.lang.reflect.Field;
 +import java.util.ArrayList;
 +import java.util.Arrays;
 +import java.util.Collection;
 +import java.util.Date;
 +import java.util.List;
 +import org.apache.syncope.client.console.commons.Constants;
 +import org.apache.syncope.client.console.pages.DisplayAttributesModalPage;
 +import org.apache.syncope.client.console.pages.EditUserModalPage;
 +import org.apache.syncope.client.console.pages.ResultStatusModalPage;
 +import org.apache.syncope.client.console.pages.StatusModalPage;
 +import org.apache.syncope.client.console.rest.AbstractSubjectRestClient;
 +import org.apache.syncope.client.console.rest.SchemaRestClient;
 +import org.apache.syncope.client.console.rest.UserRestClient;
 +import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
 +import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.AttrColumn;
 +import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
 +import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.TokenColumn;
 +import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 +import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink.ActionType;
 +import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksPanel;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +import org.apache.syncope.common.lib.to.AbstractAttributableTO;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.AttributableType;
 +import org.apache.syncope.common.lib.types.SchemaType;
 +import org.apache.wicket.Page;
 +import org.apache.wicket.PageReference;
 +import org.apache.wicket.ajax.AjaxRequestTarget;
 +import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
 +import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
 +import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
 +import org.apache.wicket.model.IModel;
 +import org.apache.wicket.model.Model;
 +import org.apache.wicket.model.ResourceModel;
 +import org.apache.wicket.spring.injection.annot.SpringBean;
 +import org.springframework.util.ReflectionUtils;
 +
 +public class UserSearchResultPanel extends AbstractSearchResultPanel {
 +
 +    private static final long serialVersionUID = -905187144506842332L;
 +
 +    private final static String PAGEID = "Users";
 +
 +    @SpringBean
 +    private SchemaRestClient schemaRestClient;
 +
 +    private final List<String> pSchemaNames;
 +
 +    private final List<String> dSchemaNames;
 +
-     private final List<String> vSchemaNames;
- 
 +    public <T extends AbstractAttributableTO> UserSearchResultPanel(final String id, final boolean filtered,
 +            final String fiql, final PageReference callerRef, final AbstractSubjectRestClient restClient) {
 +
 +        super(id, filtered, fiql, callerRef, restClient);
 +
 +        this.pSchemaNames = schemaRestClient.getPlainSchemaNames(AttributableType.USER);
 +        this.dSchemaNames = schemaRestClient.getDerSchemaNames(AttributableType.USER);
-         this.vSchemaNames = schemaRestClient.getVirSchemaNames(AttributableType.USER);
 +
 +        initResultTable();
 +    }
 +
 +    @Override
 +    protected List<IColumn<AbstractAttributableTO, String>> getColumns() {
 +        final List<IColumn<AbstractAttributableTO, String>> columns = new ArrayList<>();
 +
 +        for (String name : prefMan.getList(getRequest(), Constants.PREF_USERS_DETAILS_VIEW)) {
 +            final Field field = ReflectionUtils.findField(UserTO.class, name);
 +
 +            if ("token".equalsIgnoreCase(name)) {
 +                columns.add(new TokenColumn("token"));
 +            } else if (field != null && field.getType().equals(Date.class)) {
 +                columns.add(new DatePropertyColumn<AbstractAttributableTO>(new ResourceModel(name, name), name, name));
 +            } else {
 +                columns.add(
 +                        new PropertyColumn<AbstractAttributableTO, String>(new ResourceModel(name, name), name, name));
 +            }
 +        }
 +
 +        for (String name : prefMan.getList(getRequest(), Constants.PREF_USERS_ATTRIBUTES_VIEW)) {
 +            if (pSchemaNames.contains(name)) {
 +                columns.add(new AttrColumn(name, SchemaType.PLAIN));
 +            }
 +        }
 +
 +        for (String name : prefMan.getList(getRequest(), Constants.PREF_USERS_DERIVED_ATTRIBUTES_VIEW)) {
 +            if (dSchemaNames.contains(name)) {
 +                columns.add(new AttrColumn(name, SchemaType.DERIVED));
 +            }
 +        }
 +
-         for (String name : prefMan.getList(getRequest(), Constants.PREF_USERS_VIRTUAL_ATTRIBUTES_VIEW)) {
-             if (vSchemaNames.contains(name)) {
-                 columns.add(new AttrColumn(name, SchemaType.VIRTUAL));
-             }
-         }
- 
 +        // Add defaults in case of no selection
 +        if (columns.isEmpty()) {
 +            for (String name : DisplayAttributesModalPage.DEFAULT_SELECTION) {
 +                columns.add(
 +                        new PropertyColumn<AbstractAttributableTO, String>(new ResourceModel(name, name), name, name));
 +            }
 +
 +            prefMan.setList(getRequest(), getResponse(), Constants.PREF_USERS_DETAILS_VIEW,
 +                    Arrays.asList(DisplayAttributesModalPage.DEFAULT_SELECTION));
 +        }
 +
 +        columns.add(new ActionColumn<AbstractAttributableTO, String>(new ResourceModel("actions", "")) {
 +
 +            private static final long serialVersionUID = -3503023501954863131L;
 +
 +            @Override
 +            public ActionLinksPanel getActions(final String componentId, final IModel<AbstractAttributableTO> model) {
 +
 +                final ActionLinksPanel panel = new ActionLinksPanel(componentId, model, page.getPageReference());
 +
 +                panel.add(new ActionLink() {
 +
 +                    private static final long serialVersionUID = -7978723352517770644L;
 +
 +                    @Override
 +                    public void onClick(final AjaxRequestTarget target) {
 +                        statusmodal.setPageCreator(new ModalWindow.PageCreator() {
 +
 +                            private static final long serialVersionUID = -7834632442532690940L;
 +
 +                            @Override
 +                            public Page createPage() {
 +                                return new StatusModalPage<UserTO>(
 +                                        page.getPageReference(), statusmodal, (UserTO) model.getObject());
 +                            }
 +                        });
 +
 +                        statusmodal.show(target);
 +                    }
 +                }, ActionLink.ActionType.MANAGE_RESOURCES, PAGEID);
 +
 +                panel.add(new ActionLink() {
 +
 +                    private static final long serialVersionUID = -7978723352517770644L;
 +
 +                    @Override
 +                    public void onClick(final AjaxRequestTarget target) {
 +                        statusmodal.setPageCreator(new ModalWindow.PageCreator() {
 +
 +                            private static final long serialVersionUID = -7834632442532690940L;
 +
 +                            @Override
 +                            public Page createPage() {
 +                                return new StatusModalPage<UserTO>(
 +                                        page.getPageReference(), statusmodal, (UserTO) model.getObject(), true);
 +                            }
 +                        });
 +
 +                        statusmodal.show(target);
 +                    }
 +                }, ActionLink.ActionType.ENABLE, PAGEID);
 +
 +                panel.add(new ActionLink() {
 +
 +                    private static final long serialVersionUID = -7978723352517770644L;
 +
 +                    @Override
 +                    public void onClick(final AjaxRequestTarget target) {
 +                        editmodal.setPageCreator(new ModalWindow.PageCreator() {
 +
 +                            private static final long serialVersionUID = -7834632442532690940L;
 +
 +                            @Override
 +                            public Page createPage() {
 +                                // SYNCOPE-294: re-read userTO before edit
 +                                UserTO userTO = ((UserRestClient) restClient).read(model.getObject().getKey());
 +                                return new EditUserModalPage(page.getPageReference(), editmodal, userTO);
 +                            }
 +                        });
 +
 +                        editmodal.show(target);
 +                    }
 +                }, ActionLink.ActionType.EDIT, PAGEID);
 +
 +                panel.add(new ActionLink() {
 +
 +                    private static final long serialVersionUID = -7978723352517770644L;
 +
 +                    @Override
 +                    public void onClick(final AjaxRequestTarget target) {
 +                        try {
 +                            final UserTO userTO = (UserTO) restClient.
 +                                    delete(model.getObject().getETagValue(), model.getObject().getKey());
 +
 +                            page.setModalResult(true);
 +
 +                            editmodal.setPageCreator(new ModalWindow.PageCreator() {
 +
 +                                private static final long serialVersionUID = -7834632442532690940L;
 +
 +                                @Override
 +                                public Page createPage() {
 +                                    return new ResultStatusModalPage.Builder(editmodal, userTO).build();
 +                                }
 +                            });
 +
 +                            editmodal.show(target);
 +                        } catch (SyncopeClientException scce) {
 +                            error(getString(Constants.OPERATION_ERROR) + ": " + scce.getMessage());
 +                            feedbackPanel.refresh(target);
 +                        }
 +                    }
 +                }, ActionLink.ActionType.DELETE, PAGEID);
 +
 +                return panel;
 +            }
 +
 +            @Override
 +            public ActionLinksPanel getHeader(final String componentId) {
 +                final ActionLinksPanel panel = new ActionLinksPanel(componentId, new Model(), page.getPageReference());
 +
 +                panel.add(new ActionLink() {
 +
 +                    private static final long serialVersionUID = -7978723352517770644L;
 +
 +                    @Override
 +                    public void onClick(final AjaxRequestTarget target) {
 +                        displaymodal.setPageCreator(new ModalWindow.PageCreator() {
 +
 +                            private static final long serialVersionUID = -7834632442532690940L;
 +
 +                            @Override
 +                            public Page createPage() {
 +                                return new DisplayAttributesModalPage(page.getPageReference(), displaymodal,
-                                         pSchemaNames, dSchemaNames, vSchemaNames);
++                                        pSchemaNames, dSchemaNames);
 +                            }
 +                        });
 +
 +                        displaymodal.show(target);
 +                    }
 +                }, ActionLink.ActionType.CHANGE_VIEW, PAGEID);
 +
 +                panel.add(new ActionLink() {
 +
 +                    private static final long serialVersionUID = -7978723352517770644L;
 +
 +                    @Override
 +                    public void onClick(final AjaxRequestTarget target) {
 +                        if (target != null) {
 +                            target.add(container);
 +                        }
 +                    }
 +                }, ActionLink.ActionType.RELOAD, PAGEID, "list");
 +
 +                return panel;
 +            }
 +        });
 +
 +        return columns;
 +    }
 +
 +    @Override
 +    protected <T extends AbstractAttributableTO> Collection<ActionType> getBulkActions() {
 +        final List<ActionType> bulkActions = new ArrayList<ActionType>();
 +
 +        bulkActions.add(ActionType.DELETE);
 +        bulkActions.add(ActionType.SUSPEND);
 +        bulkActions.add(ActionType.REACTIVATE);
 +
 +        return bulkActions;
 +    }
 +
 +    @Override
 +    protected String getPageId() {
 +        return PAGEID;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/client/old_console/src/main/java/org/apache/syncope/client/console/rest/UserRestClient.java
----------------------------------------------------------------------
diff --cc client/old_console/src/main/java/org/apache/syncope/client/console/rest/UserRestClient.java
index bca0e28,0000000..fff02bb
mode 100644,000000..100644
--- a/client/old_console/src/main/java/org/apache/syncope/client/console/rest/UserRestClient.java
+++ b/client/old_console/src/main/java/org/apache/syncope/client/console/rest/UserRestClient.java
@@@ -1,228 -1,0 +1,228 @@@
 +/*
 + * 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.client.console.rest;
 +
 +import java.util.List;
 +import javax.ws.rs.core.Response;
 +import org.apache.syncope.client.console.commons.status.StatusBean;
 +import org.apache.syncope.client.console.commons.status.StatusUtils;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +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.BulkAction;
 +import org.apache.syncope.common.lib.to.BulkActionResult;
 +import org.apache.syncope.common.lib.to.ConnObjectTO;
 +import org.apache.syncope.common.lib.to.UserTO;
 +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.wrap.ResourceName;
 +import org.apache.syncope.common.rest.api.CollectionWrapper;
 +import org.apache.syncope.common.rest.api.service.ResourceService;
 +import org.apache.syncope.common.rest.api.service.UserService;
 +import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 +import org.springframework.stereotype.Component;
 +
 +/**
 + * Console client for invoking rest users services.
 + */
 +@Component
 +public class UserRestClient extends AbstractSubjectRestClient {
 +
 +    private static final long serialVersionUID = -1575748964398293968L;
 +
 +    @Override
 +    public int count() {
 +        return getService(UserService.class).list(1, 1).getTotalCount();
 +    }
 +
 +    @Override
 +    public List<UserTO> list(final int page, final int size, final SortParam<String> sort) {
-         return getService(UserService.class).list(page, size, toOrderBy(sort)).getResult();
++        return getService(UserService.class).list(page, size, toOrderBy(sort), false).getResult();
 +    }
 +
 +    public UserTO create(final UserTO userTO, final boolean storePassword) {
 +        Response response = getService(UserService.class).create(userTO, storePassword);
 +        return response.readEntity(UserTO.class);
 +    }
 +
 +    public UserTO update(final String etag, final UserMod userMod) {
 +        UserTO result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            result = service.update(userMod.getKey(), userMod).readEntity(UserTO.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +
 +    @Override
 +    public UserTO delete(final String etag, final Long id) {
 +        UserTO result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            result = service.delete(id).readEntity(UserTO.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +
 +    public UserTO read(final Long id) {
 +        UserTO userTO = null;
 +        try {
 +            userTO = getService(UserService.class).read(id);
 +        } catch (SyncopeClientException e) {
 +            LOG.error("While reading a user", e);
 +        }
 +        return userTO;
 +    }
 +
 +    @Override
 +    public int searchCount(final String fiql) {
 +        return getService(UserService.class).search(fiql, 1, 1).getTotalCount();
 +    }
 +
 +    @Override
 +    public List<UserTO> search(final String fiql, final int page, final int size, final SortParam<String> sort) {
-         return getService(UserService.class).search(fiql, page, size, toOrderBy(sort)).getResult();
++        return getService(UserService.class).search(fiql, page, size, toOrderBy(sort), false).getResult();
 +    }
 +
 +    @Override
 +    public ConnObjectTO getConnectorObject(final String resourceName, final Long id) {
 +        return getService(ResourceService.class).getConnectorObject(resourceName, SubjectType.USER, id);
 +    }
 +
 +    public void suspend(final String etag, final long userId, final List<StatusBean> statuses) {
 +        StatusMod statusMod = StatusUtils.buildStatusMod(statuses, false);
 +        statusMod.setType(StatusMod.ModType.SUSPEND);
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            service.status(userId, statusMod);
 +            resetClient(UserService.class);
 +        }
 +    }
 +
 +    public void reactivate(final String etag, final long userId, final List<StatusBean> statuses) {
 +        StatusMod statusMod = StatusUtils.buildStatusMod(statuses, true);
 +        statusMod.setType(StatusMod.ModType.REACTIVATE);
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            service.status(userId, statusMod);
 +            resetClient(UserService.class);
 +        }
 +    }
 +
 +    @Override
 +    public BulkActionResult bulkAction(final BulkAction action) {
 +        return getService(UserService.class).bulk(action);
 +    }
 +
 +    public void unlink(final String etag, final long userId, final List<StatusBean> statuses) {
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            service.bulkDeassociation(userId, ResourceDeassociationActionType.UNLINK,
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
 +                            ResourceName.class));
 +            resetClient(UserService.class);
 +        }
 +    }
 +
 +    public void link(final String etag, final long userId, final List<StatusBean> statuses) {
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +
 +            final ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +            associationMod.getTargetResources().addAll(
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
 +                            ResourceName.class));
 +            service.bulkAssociation(userId, ResourceAssociationActionType.LINK, associationMod);
 +
 +            resetClient(UserService.class);
 +        }
 +    }
 +
 +    public BulkActionResult deprovision(final String etag, final long userId, final List<StatusBean> statuses) {
 +        BulkActionResult result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            result = service.bulkDeassociation(userId, ResourceDeassociationActionType.DEPROVISION,
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
 +                            ResourceName.class)).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +
 +    public BulkActionResult provision(final String etag, final long userId,
 +            final List<StatusBean> statuses, final boolean changepwd, final String password) {
 +
 +        BulkActionResult result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +
 +            final ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +            associationMod.getTargetResources().addAll(
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
 +                            ResourceName.class));
 +            associationMod.setChangePwd(changepwd);
 +            associationMod.setPassword(password);
 +
 +            result = service.bulkAssociation(userId, ResourceAssociationActionType.PROVISION, associationMod).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +
 +    public BulkActionResult unassign(final String etag, final long userId, final List<StatusBean> statuses) {
 +        BulkActionResult result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            result = service.bulkDeassociation(userId, ResourceDeassociationActionType.UNASSIGN,
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
 +                            ResourceName.class)).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +
 +    public BulkActionResult assign(final String etag, final long userId,
 +            final List<StatusBean> statuses, final boolean changepwd, final String password) {
 +
 +        BulkActionResult result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +
 +            final ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +            associationMod.getTargetResources().addAll(
 +                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
 +                            ResourceName.class));
 +            associationMod.setChangePwd(changepwd);
 +            associationMod.setPassword(password);
 +
 +            result = service.bulkAssociation(userId, ResourceAssociationActionType.ASSIGN, associationMod).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/client/old_console/src/main/resources/org/apache/syncope/client/console/pages/DisplayAttributesModalPage.html
----------------------------------------------------------------------
diff --cc client/old_console/src/main/resources/org/apache/syncope/client/console/pages/DisplayAttributesModalPage.html
index 998483d,0000000..4e4064c
mode 100644,000000..100644
--- a/client/old_console/src/main/resources/org/apache/syncope/client/console/pages/DisplayAttributesModalPage.html
+++ b/client/old_console/src/main/resources/org/apache/syncope/client/console/pages/DisplayAttributesModalPage.html
@@@ -1,132 -1,0 +1,130 @@@
 +<!--
 +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.
 +-->
 +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
 +  <wicket:head>
 +    <style>
 +      div.group{
 +        width:450px;
 +      }
 +
 +      div.group div{
 +        width:150;
 +        height: 25px;
 +        float:left;
 +      }
 +
 +      div.group div input {
 +        width: 30px;
 +      }
 +
 +      div#attributes-view {
 +        display:  block;
 +        clear:  both;
 +        float: none;
 +        overflow: auto;
 +        margin-top: 0px;
 +        margin-bottom: 20px;
 +        margin-left: 10px;
 +        margin-right: 10px;
 +      }
 +
 +      .submit{
 +        display:  block;
 +        clear:  both;
 +        float: none;
 +        margin-left: 10px;
 +      }
 +
 +      span.grouplabel{
 +        display:block;
 +        clear: both;
 +        margin-left: 10px;
 +        margin-bottom: 10px;
 +        font-weight: bold;
 +      }
 +    </style>
 +  </wicket:head>
 +  <wicket:extend>
 +    <form wicket:id="form">
 +      <div id="attributes-view">
 +        <p class="ui-widget ui-corner-all ui-widget-header">
 +          <wicket:message key="title"/>
 +        </p>
 +
 +        <span class="grouplabel"><wicket:message key="plainSchemas"/></span>
 +        <span wicket:id="dCheckGroup">
 +          <div class="group">
 +            <div wicket:id="details">
 +              <input type="checkbox" wicket:id="dcheck"/>
 +              <span wicket:id="dname">[schema name]</span>
 +            </div>
 +          </div>
 +        </span>
 +
 +        <span wicket:id="plainSchemas">[schemas]</span>
 +
 +        <span wicket:id="dschemas">[derived schemas]</span>
 +
-         <span wicket:id="vschemas">[virtual schemas]</span>
- 
 +      </div>
 +
 +      <wicket:fragment wicket:id="sfragment">
 +        <span wicket:id="psCheckGroup">
 +          <div class="group">
 +            <div wicket:id="plainSchemas">
 +              <input type="checkbox" wicket:id="scheck"/>
 +              <span wicket:id="sname">[schema name]</span>
 +            </div>
 +          </div>
 +        </span>
 +      </wicket:fragment>
 +
 +      <wicket:fragment wicket:id="dsfragment">
 +        <span class="grouplabel"><wicket:message key="derSchemas"/></span>
 +        <span wicket:id="dsCheckGroup">
 +          <div class="group">
 +            <div wicket:id="derSchemas">
 +              <input type="checkbox" wicket:id="dscheck"/>
 +              <span wicket:id="dsname">[schema name]</span>
 +            </div>
 +          </div>
 +        </span>
 +      </wicket:fragment>
 +
 +      <wicket:fragment wicket:id="vsfragment">
 +        <span class="grouplabel"><wicket:message key="virSchemas"/></span>
 +        <span wicket:id="vsCheckGroup">
 +          <div class="group">
 +            <div wicket:id="virSchemas">
 +              <input type="checkbox" wicket:id="vscheck"/>
 +              <span wicket:id="vsname">[schema name]</span>
 +            </div>
 +          </div>
 +        </span>
 +      </wicket:fragment>
 +
 +      <wicket:fragment wicket:id="emptyFragment">
 +      </wicket:fragment>
 +
 +      <div class="submit">
 +        <input type="submit" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" wicket:id="submit"/>
 +        <input type="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" wicket:id="cancel"/>
 +      </div>
 +    </form>
 +  </wicket:extend>
 +</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ListQuery.java
----------------------------------------------------------------------
diff --cc common/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ListQuery.java
index 1a85fe4,0000000..96fe514
mode 100644,000000..100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ListQuery.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ListQuery.java
@@@ -1,86 -1,0 +1,98 @@@
 +/*
 + * 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.common.rest.api.beans;
 +
 +import java.io.Serializable;
 +import javax.validation.constraints.Min;
 +import javax.ws.rs.DefaultValue;
 +import javax.ws.rs.QueryParam;
 +import org.apache.commons.lang3.builder.EqualsBuilder;
 +import org.apache.commons.lang3.builder.HashCodeBuilder;
 +import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
 +import org.apache.commons.lang3.builder.ToStringStyle;
 +import org.apache.syncope.common.rest.api.service.JAXRSService;
 +
 +public class ListQuery implements Serializable {
 +
 +    private static final long serialVersionUID = -371488230250055359L;
 +
 +    private Integer page;
 +
 +    private Integer size;
 +
 +    private String orderBy;
 +
++    private Boolean details;
++
 +    public Integer getPage() {
 +        return page;
 +    }
 +
 +    @Min(1)
 +    @QueryParam(JAXRSService.PARAM_PAGE)
 +    @DefaultValue("1")
 +    public void setPage(final Integer page) {
 +        this.page = page;
 +    }
 +
 +    public Integer getSize() {
 +        return size;
 +    }
 +
 +    @Min(1)
 +    @QueryParam(JAXRSService.PARAM_SIZE)
 +    @DefaultValue("25")
 +    public void setSize(final Integer size) {
 +        this.size = size;
 +    }
 +
 +    @QueryParam(JAXRSService.PARAM_ORDERBY)
 +    public String getOrderBy() {
 +        return orderBy;
 +    }
 +
 +    public void setOrderBy(final String orderBy) {
 +        this.orderBy = orderBy;
 +    }
 +
++    @QueryParam(JAXRSService.PARAM_DETAILS)
++    @DefaultValue("true")
++    public boolean isDetails() {
++        return details == null ? true : details;
++    }
++
++    public void setDetails(final boolean details) {
++        this.details = details;
++    }
++
 +    @Override
 +    public boolean equals(final Object obj) {
 +        return EqualsBuilder.reflectionEquals(this, obj);
 +    }
 +
 +    @Override
 +    public int hashCode() {
 +        return HashCodeBuilder.reflectionHashCode(this);
 +    }
 +
 +    @Override
 +    public String toString() {
 +        return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE);
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/JAXRSService.java
----------------------------------------------------------------------
diff --cc common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/JAXRSService.java
index bfd166a,0000000..c6c73d5
mode 100644,000000..100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/JAXRSService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/JAXRSService.java
@@@ -1,31 -1,0 +1,33 @@@
 +/*
 + * 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.common.rest.api.service;
 +
 +public interface JAXRSService {
 +
 +    String PARAM_FIQL = "fiql";
 +
 +    String PARAM_PAGE = "page";
 +
 +    String PARAM_SIZE = "size";
 +
 +    String PARAM_ORDERBY = "orderby";
 +
++    String PARAM_DETAILS = "details";
++
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
----------------------------------------------------------------------
diff --cc core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
index d24b08e,0000000..6f66113
mode 100644,000000..100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
@@@ -1,86 -1,0 +1,92 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.syncope.core.logic;
 +
 +import java.util.Collection;
 +import java.util.HashSet;
 +import java.util.List;
 +import java.util.Set;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Predicate;
 +import org.apache.syncope.common.lib.mod.AnyMod;
 +import org.apache.syncope.common.lib.to.AnyTO;
 +import org.apache.syncope.core.misc.RealmUtils;
 +import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 +import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 +
 +public abstract class AbstractAnyLogic<TO extends AnyTO, MOD extends AnyMod>
 +        extends AbstractResourceAssociator<TO> {
 +
 +    private static class StartsWithPredicate implements Predicate<String> {
 +
 +        private final Collection<String> targets;
 +
 +        public StartsWithPredicate(final Collection<String> targets) {
 +            this.targets = targets;
 +        }
 +
 +        @Override
 +        public boolean evaluate(final String realm) {
 +            return CollectionUtils.exists(targets, new Predicate<String>() {
 +
 +                @Override
 +                public boolean evaluate(final String target) {
 +                    return realm.startsWith(target);
 +                }
 +            });
 +        }
 +
 +    }
 +
 +    protected Set<String> getEffectiveRealms(
 +            final Set<String> allowedRealms, final Collection<String> requestedRealms) {
 +
 +        final Set<String> allowed = RealmUtils.normalize(allowedRealms);
 +        final Set<String> requested = RealmUtils.normalize(requestedRealms);
 +
 +        Set<String> effective = new HashSet<>();
 +        CollectionUtils.select(requested, new StartsWithPredicate(allowed), effective);
 +        CollectionUtils.select(allowed, new StartsWithPredicate(requested), effective);
 +
 +        return effective;
 +    }
 +
 +    public abstract TO read(Long key);
 +
 +    public abstract int count(List<String> realms);
 +
 +    public abstract TO create(TO anyTO);
 +
 +    public abstract TO update(MOD anyMod);
 +
 +    public abstract TO delete(Long key);
 +
-     public abstract List<TO> list(int page, int size, List<OrderByClause> orderBy, List<String> realms);
++    public abstract List<TO> list(
++            int page, int size, List<OrderByClause> orderBy,
++            List<String> realms,
++            boolean details);
 +
 +    public abstract List<TO> search(
-             SearchCond searchCondition, int page, int size, List<OrderByClause> orderBy, List<String> realms);
++            SearchCond searchCondition,
++            int page, int size, List<OrderByClause> orderBy,
++            List<String> realms,
++            boolean details);
 +
 +    public abstract int searchCount(SearchCond searchCondition, List<String> realms);
 +}