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);
+}