diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000000..849fbf93be
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,30 @@
+## References
+_Add references/links to any related tickets or PRs. These may include:_
+* Link to [JIRA](https://jira.lyrasis.org/projects/DS/summary) ticket(s), if any
+* Link to [REST Contract](https://github.com/DSpace/Rest7Contract) or an open REST Contract PR, if any
+* Link to [Angular issue or PR](https://github.com/DSpace/dspace-angular/issues) related to this PR, if any
+
+## Description
+Short summary of changes (1-2 sentences).
+
+## Instructions for Reviewers
+Please add a more detailed description of the changes made by your PR. At a minimum, providing a bulleted list of changes in your PR is helpful to reviewers.
+
+List of changes in this PR:
+* First, ...
+* Second, ...
+
+**Include guidance for how to test or review your PR.** This may include: steps to reproduce a bug, screenshots or description of a new feature, or reasons behind specific changes.
+
+## Checklist
+_This checklist provides a reminder of what we are going to look for when reviewing your PR. You need not complete this checklist prior to creating your PR (draft PRs are always welcome). If you are unsure about an item in the checklist, don't hesitate to ask. We're here to help!_
+
+- [ ] My PR is small in size (e.g. less than 1,000 lines of code, not including comments & integration tests). Exceptions may be made if previously agreed upon.
+- [ ] My PR passes Checkstyle validation based on the [Code Style Guide](https://wiki.lyrasis.org/display/DSPACE/Code+Style+Guide)
+- [ ] My PR includes Javadoc for _all new (or modified) public methods and classes_. It also includes Javadoc for large or complex private methods.
+- [ ] My PR passes all tests and includes new/updated Unit or Integration Tests for any bug fixes, improvements or new features. A few reminders about what constitutes good tests:
+ * Include tests for different user types, including: (1) Anonymous user, (2) Logged in user (non-admin), and (3) Administrator.
+ * Include tests for known error scenarios and error codes (e.g. `400 Bad Request`, `401 Unauthorized`, `403 Forbidden`, `404 Not Found`, etc)
+ * For bug fixes, include a test that reproduces the bug and proves it is fixed. For clarity, it may be useful to provide the test in a separate commit from the bug fix.
+- [ ] If my PR includes new, third-party dependencies (in any `pom.xml`), I've made sure their licenses align with the [DSpace BSD License](https://github.com/DSpace/DSpace/blob/master/LICENSE) based on the [Licensing of Contributions](https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines#CodeContributionGuidelines-LicensingofContributions) documentation.
+- [ ] If my PR modifies the REST API, I've linked to the REST Contract page (or open PR) related to this change.
diff --git a/LICENSE b/LICENSE
index 4ae8679ae7..b7cb98fe83 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,6 @@
-DSpace source code license:
+DSpace source code BSD License:
-
-Copyright (c) 2002-2016, DuraSpace. All rights reserved.
+Copyright (c) 2002-2020, LYRASIS. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@@ -34,7 +33,7 @@ DAMAGE.
DSpace uses third-party libraries which may be distributed under
-different licenses to the above. Information about these licenses
-is detailed in the LICENSES_THIRD_PARTY file at the root of the source
-tree. You must agree to the terms of these licenses, in addition to
+different licenses to the above. Information about these licenses
+is detailed in the LICENSES_THIRD_PARTY file at the root of the source
+tree. You must agree to the terms of these licenses, in addition to
the above DSpace source code license, in order to use this software.
diff --git a/NOTICE b/NOTICE
index ffb5224207..6743fea511 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,15 +1,18 @@
+Licensing Notices
+=================
-Licensing Notice
+[July 2019] DuraSpace joined with LYRASIS (another 501(c)3 organization) in July 2019.
+LYRASIS holds the copyrights of DuraSpace.
-Fedora Commons joined with the DSpace Foundation and began operating under
+[July 2009] Fedora Commons joined with the DSpace Foundation and began operating under
the new name DuraSpace in July 2009. DuraSpace holds the copyrights of
the DSpace Foundation, Inc.
-The DSpace Foundation, Inc. is a 501(c)3 corporation established in July 2007
-with a mission to promote and advance the dspace platform enabling management,
-access and preservation of digital works. The Foundation was able to transfer
-the legal copyright from Hewlett-Packard Company (HP) and Massachusetts
-Institute of Technology (MIT) to the DSpace Foundation in October 2007. Many
-of the files in the source code may contain a copyright statement stating HP
-and MIT possess the copyright, in these instances please note that the copy
+[July 2007] The DSpace Foundation, Inc. is a 501(c)3 corporation established in July 2007
+with a mission to promote and advance the dspace platform enabling management,
+access and preservation of digital works. The Foundation was able to transfer
+the legal copyright from Hewlett-Packard Company (HP) and Massachusetts
+Institute of Technology (MIT) to the DSpace Foundation in October 2007. Many
+of the files in the source code may contain a copyright statement stating HP
+and MIT possess the copyright, in these instances please note that the copy
right has transferred to the DSpace foundation, and subsequently to DuraSpace.
diff --git a/checkstyle.xml b/checkstyle.xml
index 554e187641..815edaec7b 100644
--- a/checkstyle.xml
+++ b/checkstyle.xml
@@ -44,15 +44,16 @@ For more information on CheckStyle configurations below, see: http://checkstyle.
with @SuppressWarnings. See also SuppressWarningsHolder below -->
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
@@ -94,11 +95,8 @@ For more information on CheckStyle configurations below, see: http://checkstyle.
-
-
-
diff --git a/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java b/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java
index 04db15eb4a..6c4271e1f2 100644
--- a/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java
+++ b/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java
@@ -190,10 +190,11 @@ public class AuthorizeUtil {
public static void authorizeManageCCLicense(Context context, Item item)
throws AuthorizeException, SQLException {
AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
+ CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
ItemService itemService = ContentServiceFactory.getInstance().getItemService();
try {
- authorizeService.authorizeAction(context, item, Constants.ADD);
- authorizeService.authorizeAction(context, item, Constants.REMOVE);
+ authorizeService.authorizeAction(context, item, Constants.ADD, false);
+ authorizeService.authorizeAction(context, item, Constants.REMOVE, false);
} catch (AuthorizeException authex) {
if (AuthorizeConfiguration.canItemAdminManageCCLicense()) {
authorizeService
@@ -202,8 +203,10 @@ public class AuthorizeUtil {
authorizeService.authorizeAction(context, itemService
.getParentObject(context, item), Constants.ADMIN);
} else if (AuthorizeConfiguration.canCommunityAdminManageCCLicense()) {
- authorizeService.authorizeAction(context, itemService
- .getParentObject(context, item), Constants.ADMIN);
+ Collection collection = (Collection) itemService
+ .getParentObject(context, item);
+ authorizeService.authorizeAction(context, collectionService.getParentObject(context, collection),
+ Constants.ADMIN);
} else {
requireAdminRole(context);
}
diff --git a/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java
index 9132dbe311..97f25cb2b2 100644
--- a/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java
@@ -20,7 +20,6 @@ import com.sun.syndication.feed.module.opensearch.OpenSearchModule;
import com.sun.syndication.feed.module.opensearch.entity.OSQuery;
import com.sun.syndication.feed.module.opensearch.impl.OpenSearchModuleImpl;
import com.sun.syndication.io.FeedException;
-
import org.apache.logging.log4j.Logger;
import org.dspace.app.util.service.OpenSearchService;
import org.dspace.content.DSpaceObject;
diff --git a/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java b/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java
index 2ff0e457a8..92c698fe9f 100644
--- a/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java
+++ b/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java
@@ -34,6 +34,7 @@ import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.MetadataFieldService;
import org.dspace.content.service.MetadataSchemaService;
import org.dspace.core.Context;
+import org.dspace.core.Utils;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.eperson.factory.EPersonServiceFactory;
@@ -492,35 +493,22 @@ public class ShibAuthentication implements AuthenticationMethod {
boolean lazySession = configurationService.getBooleanProperty("authentication-shibboleth.lazysession", false);
if ( lazySession ) {
- String shibURL = configurationService.getProperty("authentication-shibboleth.lazysession.loginurl");
- boolean forceHTTPS =
- configurationService.getBooleanProperty("authentication-shibboleth.lazysession.secure",true);
+ String shibURL = getShibURL(request);
- // Shibboleth authentication initiator
- if (shibURL == null || shibURL.length() == 0) {
- shibURL = "/Shibboleth.sso/Login";
+ // Determine the client redirect URL, where to redirect after authenticating.
+ String redirectUrl = null;
+ if (request.getHeader("Referer") != null && StringUtils.isNotBlank(request.getHeader("Referer"))) {
+ redirectUrl = request.getHeader("Referer");
+ } else if (request.getHeader("X-Requested-With") != null
+ && StringUtils.isNotBlank(request.getHeader("X-Requested-With"))) {
+ redirectUrl = request.getHeader("X-Requested-With");
}
- shibURL = shibURL.trim();
- // Determine the return URL, where shib will send the user after authenticating. We need it to go back
- // to DSpace's shibboleth-login url so the we will extract the user's information and locally
- // authenticate them.
- String host = request.getServerName();
- int port = request.getServerPort();
- String contextPath = request.getContextPath();
-
- String returnURL = request.getHeader("Referer");
- if (returnURL == null) {
- if (request.isSecure() || forceHTTPS) {
- returnURL = "https://";
- } else {
- returnURL = "http://";
- }
- returnURL += host;
- if (!(port == 443 || port == 80)) {
- returnURL += ":" + port;
- }
- }
+ // Determine the server return URL, where shib will send the user after authenticating.
+ // We need it to go back to DSpace's shibboleth-login url so we will extract the user's information
+ // and locally authenticate them.
+ String returnURL = configurationService.getProperty("dspace.server.url") + "/api/authn/shibboleth"
+ + ((redirectUrl != null) ? "?redirectUrl=" + redirectUrl : "");
try {
shibURL += "?target=" + URLEncoder.encode(returnURL, "UTF-8");
@@ -1258,6 +1246,23 @@ public class ShibAuthentication implements AuthenticationMethod {
return valueList;
}
+ private String getShibURL(HttpServletRequest request) {
+ String shibURL = configurationService.getProperty("authentication-shibboleth.lazysession.loginurl",
+ "/Shibboleth.sso/Login");
+ boolean forceHTTPS =
+ configurationService.getBooleanProperty("authentication-shibboleth.lazysession.secure", true);
+
+ // Shibboleth url must be absolute
+ if (shibURL.startsWith("/")) {
+ String serverUrl = Utils.getBaseUrl(configurationService.getProperty("dspace.server.url"));
+ shibURL = serverUrl + shibURL;
+ if ((request.isSecure() || forceHTTPS) && shibURL.startsWith("http://")) {
+ shibURL = shibURL.replace("http://", "https://");
+ }
+ }
+ return shibURL;
+
+ }
}
diff --git a/dspace-api/src/main/java/org/dspace/authorize/AuthorizeConfiguration.java b/dspace-api/src/main/java/org/dspace/authorize/AuthorizeConfiguration.java
index 231008d267..1e051c78b9 100644
--- a/dspace-api/src/main/java/org/dspace/authorize/AuthorizeConfiguration.java
+++ b/dspace-api/src/main/java/org/dspace/authorize/AuthorizeConfiguration.java
@@ -7,7 +7,8 @@
*/
package org.dspace.authorize;
-import org.dspace.core.ConfigurationManager;
+import org.dspace.services.ConfigurationService;
+import org.dspace.utils.DSpace;
/**
* This class is responsible to provide access to the configuration of the
@@ -16,164 +17,26 @@ import org.dspace.core.ConfigurationManager;
* @author bollini
*/
public class AuthorizeConfiguration {
-
- private static boolean can_communityAdmin_group = ConfigurationManager
- .getBooleanProperty("core.authorization.community-admin.group",
- true);
-
- // subcommunities and collections
- private static boolean can_communityAdmin_createSubelement = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.community-admin.create-subelement",
- true);
-
- private static boolean can_communityAdmin_deleteSubelement = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.community-admin.delete-subelement",
- true);
-
- private static boolean can_communityAdmin_policies = ConfigurationManager
- .getBooleanProperty("core.authorization.community-admin.policies",
- true);
-
- private static boolean can_communityAdmin_adminGroup = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.community-admin.admin-group", true);
-
- private static boolean can_communityAdmin_collectionPolicies = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.community-admin.collection.policies",
- true);
-
- private static boolean can_communityAdmin_collectionTemplateItem = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.community-admin.collection.template-item",
- true);
-
- private static boolean can_communityAdmin_collectionSubmitters = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.community-admin.collection.submitters",
- true);
-
- private static boolean can_communityAdmin_collectionWorkflows = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.community-admin.collection.workflows",
- true);
-
- private static boolean can_communityAdmin_collectionAdminGroup = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.community-admin.collection.admin-group",
- true);
-
- private static boolean can_communityAdmin_itemDelete = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.community-admin.item.delete", true);
-
- private static boolean can_communityAdmin_itemWithdraw = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.community-admin.item.withdraw", true);
-
- private static boolean can_communityAdmin_itemReinstatiate = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.community-admin.item.reinstatiate",
- true);
-
- private static boolean can_communityAdmin_itemPolicies = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.community-admin.item.policies", true);
-
- // # also bundle
- private static boolean can_communityAdmin_itemCreateBitstream = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.community-admin.item.create-bitstream",
- true);
-
- private static boolean can_communityAdmin_itemDeleteBitstream = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.community-admin.item.delete-bitstream",
- true);
-
- private static boolean can_communityAdmin_itemAdminccLicense = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.community-admin.item-admin.cc-license",
- true);
-
- // # COLLECTION ADMIN
- private static boolean can_collectionAdmin_policies = ConfigurationManager
- .getBooleanProperty("core.authorization.collection-admin.policies",
- true);
-
- private static boolean can_collectionAdmin_templateItem = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.collection-admin.template-item", true);
-
- private static boolean can_collectionAdmin_submitters = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.collection-admin.submitters", true);
-
- private static boolean can_collectionAdmin_workflows = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.collection-admin.workflows", true);
-
- private static boolean can_collectionAdmin_adminGroup = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.collection-admin.admin-group", true);
-
- private static boolean can_collectionAdmin_itemDelete = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.collection-admin.item.delete", true);
-
- private static boolean can_collectionAdmin_itemWithdraw = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.collection-admin.item.withdraw", true);
-
- private static boolean can_collectionAdmin_itemReinstatiate = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.collection-admin.item.reinstatiate",
- true);
-
- private static boolean can_collectionAdmin_itemPolicies = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.collection-admin.item.policies", true);
-
- // # also bundle
- private static boolean can_collectionAdmin_itemCreateBitstream = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.collection-admin.item.create-bitstream",
- true);
-
- private static boolean can_collectionAdmin_itemDeleteBitstream = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.collection-admin.item.delete-bitstream",
- true);
-
- private static boolean can_collectionAdmin_itemAdminccLicense = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.collection-admin.item-admin.cc-license",
- true);
-
- // # ITEM ADMIN
- private static boolean can_itemAdmin_policies = ConfigurationManager
- .getBooleanProperty("core.authorization.item-admin.policies", true);
-
- // # also bundle
- private static boolean can_itemAdmin_createBitstream = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.item-admin.create-bitstream", true);
-
- private static boolean can_itemAdmin_deleteBitstream = ConfigurationManager
- .getBooleanProperty(
- "core.authorization.item-admin.delete-bitstream", true);
-
- private static boolean can_itemAdmin_ccLicense = ConfigurationManager
- .getBooleanProperty("core.authorization.item-admin.cc-license",
- true);
+ /**
+ * A static reference to the {@link ConfigurationService} see the init method for initialization
+ */
+ private static ConfigurationService configurationService;
/**
* Default constructor
*/
private AuthorizeConfiguration() { }
+ /**
+ * Complete the initialization of the class retrieving a reference to the {@link ConfigurationService}. MUST be
+ * called at the start of each method
+ */
+ private synchronized static void init() {
+ if (configurationService != null) {
+ return;
+ }
+ configurationService = new DSpace().getConfigurationService();
+ }
/**
* Are community admins allowed to create new, not strictly community
* related, group?
@@ -181,7 +44,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminPerformGroupCreation() {
- return can_communityAdmin_group;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.community-admin.group", true);
}
/**
@@ -190,7 +54,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminPerformSubelementCreation() {
- return can_communityAdmin_createSubelement;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.community-admin.create-subelement", true);
}
/**
@@ -199,7 +64,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminPerformSubelementDeletion() {
- return can_communityAdmin_deleteSubelement;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.community-admin.delete-subelement", true);
}
/**
@@ -209,7 +75,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManagePolicies() {
- return can_communityAdmin_policies;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.community-admin.policies", true);
}
/**
@@ -219,7 +86,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManageAdminGroup() {
- return can_communityAdmin_adminGroup;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.community-admin.admin-group", true);
}
/**
@@ -229,7 +97,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManageCollectionPolicies() {
- return can_communityAdmin_collectionPolicies;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.community-admin.collection.policies", true);
}
/**
@@ -239,7 +108,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManageCollectionTemplateItem() {
- return can_communityAdmin_collectionTemplateItem;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.community-admin.collection.template-item",
+ true);
}
/**
@@ -249,7 +120,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManageCollectionSubmitters() {
- return can_communityAdmin_collectionSubmitters;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.community-admin.collection.submitters",
+ true);
}
/**
@@ -259,7 +132,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManageCollectionWorkflows() {
- return can_communityAdmin_collectionWorkflows;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.community-admin.collection.workflows", true);
}
/**
@@ -269,7 +143,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManageCollectionAdminGroup() {
- return can_communityAdmin_collectionAdminGroup;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.community-admin.collection.admin-group",
+ true);
}
/**
@@ -278,7 +154,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminPerformItemDeletion() {
- return can_communityAdmin_itemDelete;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.community-admin.item.delete", true);
}
/**
@@ -287,7 +164,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminPerformItemWithdrawn() {
- return can_communityAdmin_itemWithdraw;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.community-admin.item.withdraw", true);
}
/**
@@ -297,7 +175,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminPerformItemReinstatiate() {
- return can_communityAdmin_itemReinstatiate;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.community-admin.item.reinstatiate", true);
}
/**
@@ -307,7 +186,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManageItemPolicies() {
- return can_communityAdmin_itemPolicies;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.community-admin.item.policies", true);
}
/**
@@ -317,7 +197,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminPerformBitstreamCreation() {
- return can_communityAdmin_itemCreateBitstream;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.community-admin.item.create-bitstream",
+ true);
}
/**
@@ -327,7 +209,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminPerformBitstreamDeletion() {
- return can_communityAdmin_itemDeleteBitstream;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.community-admin.item.delete-bitstream",
+ true);
}
/**
@@ -337,7 +221,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCommunityAdminManageCCLicense() {
- return can_communityAdmin_itemAdminccLicense;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.community-admin.item-admin.cc-license",
+ true);
}
/**
@@ -346,7 +232,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminManagePolicies() {
- return can_collectionAdmin_policies;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.collection-admin.policies", true);
}
/**
@@ -356,7 +243,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminManageTemplateItem() {
- return can_collectionAdmin_templateItem;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.collection-admin.template-item", true);
}
/**
@@ -366,7 +254,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminManageSubmitters() {
- return can_collectionAdmin_submitters;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.collection-admin.submitters", true);
}
/**
@@ -376,7 +265,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminManageWorkflows() {
- return can_collectionAdmin_workflows;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.collection-admin.workflows", true);
}
/**
@@ -386,7 +276,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminManageAdminGroup() {
- return can_collectionAdmin_adminGroup;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.collection-admin.admin-group", true);
}
/**
@@ -395,7 +286,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminPerformItemDeletion() {
- return can_collectionAdmin_itemDelete;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.collection-admin.item.delete", true);
}
/**
@@ -404,7 +296,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminPerformItemWithdrawn() {
- return can_collectionAdmin_itemWithdraw;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.collection-admin.item.withdraw", true);
}
/**
@@ -414,7 +307,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminPerformItemReinstatiate() {
- return can_collectionAdmin_itemReinstatiate;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.collection-admin.item.reinstatiate", true);
}
/**
@@ -424,7 +318,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminManageItemPolicies() {
- return can_collectionAdmin_itemPolicies;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.collection-admin.item.policies", true);
}
/**
@@ -434,7 +329,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminPerformBitstreamCreation() {
- return can_collectionAdmin_itemCreateBitstream;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.collection-admin.item.create-bitstream",
+ true);
}
/**
@@ -444,7 +341,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminPerformBitstreamDeletion() {
- return can_collectionAdmin_itemDeleteBitstream;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.collection-admin.item.delete-bitstream",
+ true);
}
/**
@@ -454,7 +353,9 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canCollectionAdminManageCCLicense() {
- return can_collectionAdmin_itemAdminccLicense;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.collection-admin.item-admin.cc-license",
+ true);
}
/**
@@ -463,7 +364,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canItemAdminManagePolicies() {
- return can_itemAdmin_policies;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.item-admin.policies", true);
}
/**
@@ -472,7 +374,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canItemAdminPerformBitstreamCreation() {
- return can_itemAdmin_createBitstream;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.item-admin.create-bitstream", true);
}
/**
@@ -481,7 +384,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canItemAdminPerformBitstreamDeletion() {
- return can_itemAdmin_deleteBitstream;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.item-admin.delete-bitstream", true);
}
/**
@@ -490,7 +394,8 @@ public class AuthorizeConfiguration {
* @return true/false
*/
public static boolean canItemAdminManageCCLicense() {
- return can_itemAdmin_ccLicense;
+ init();
+ return configurationService.getBooleanProperty("core.authorization.item-admin.cc-license", true);
}
}
diff --git a/dspace-api/src/main/java/org/dspace/authorize/dao/impl/ResourcePolicyDAOImpl.java b/dspace-api/src/main/java/org/dspace/authorize/dao/impl/ResourcePolicyDAOImpl.java
index 36f87f4fa6..6aa5d2bb2e 100644
--- a/dspace-api/src/main/java/org/dspace/authorize/dao/impl/ResourcePolicyDAOImpl.java
+++ b/dspace-api/src/main/java/org/dspace/authorize/dao/impl/ResourcePolicyDAOImpl.java
@@ -8,10 +8,8 @@
package org.dspace.authorize.dao.impl;
import java.sql.SQLException;
-
import java.util.List;
import java.util.UUID;
-
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
diff --git a/dspace-api/src/main/java/org/dspace/content/Bitstream.java b/dspace-api/src/main/java/org/dspace/content/Bitstream.java
index b674b4b487..39d78b0491 100644
--- a/dspace-api/src/main/java/org/dspace/content/Bitstream.java
+++ b/dspace-api/src/main/java/org/dspace/content/Bitstream.java
@@ -11,7 +11,6 @@ import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
-
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
diff --git a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java
index 83e91a2d7b..ce86d1a9a3 100644
--- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java
@@ -13,7 +13,6 @@ import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
-
import javax.annotation.Nullable;
import org.apache.commons.collections4.CollectionUtils;
diff --git a/dspace-api/src/main/java/org/dspace/content/Collection.java b/dspace-api/src/main/java/org/dspace/content/Collection.java
index 32d70aacaf..e5875b009e 100644
--- a/dspace-api/src/main/java/org/dspace/content/Collection.java
+++ b/dspace-api/src/main/java/org/dspace/content/Collection.java
@@ -12,7 +12,6 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-
import javax.persistence.Cacheable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
diff --git a/dspace-api/src/main/java/org/dspace/content/Community.java b/dspace-api/src/main/java/org/dspace/content/Community.java
index f8319b4cd1..1fe886a475 100644
--- a/dspace-api/src/main/java/org/dspace/content/Community.java
+++ b/dspace-api/src/main/java/org/dspace/content/Community.java
@@ -11,7 +11,6 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-
import javax.persistence.Cacheable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
@@ -271,4 +270,4 @@ public class Community extends DSpaceObject implements DSpaceObjectLegacySupport
return communityService;
}
-}
\ No newline at end of file
+}
diff --git a/dspace-api/src/main/java/org/dspace/content/DSpaceObject.java b/dspace-api/src/main/java/org/dspace/content/DSpaceObject.java
index 896925fb1e..a486fed82a 100644
--- a/dspace-api/src/main/java/org/dspace/content/DSpaceObject.java
+++ b/dspace-api/src/main/java/org/dspace/content/DSpaceObject.java
@@ -11,7 +11,6 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
-
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
diff --git a/dspace-api/src/main/java/org/dspace/content/Item.java b/dspace-api/src/main/java/org/dspace/content/Item.java
index 20975c8ccf..22a9a4de52 100644
--- a/dspace-api/src/main/java/org/dspace/content/Item.java
+++ b/dspace-api/src/main/java/org/dspace/content/Item.java
@@ -13,7 +13,6 @@ import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
diff --git a/dspace-api/src/main/java/org/dspace/content/WorkspaceItem.java b/dspace-api/src/main/java/org/dspace/content/WorkspaceItem.java
index e2ded4815d..f55dfaf2da 100644
--- a/dspace-api/src/main/java/org/dspace/content/WorkspaceItem.java
+++ b/dspace-api/src/main/java/org/dspace/content/WorkspaceItem.java
@@ -10,7 +10,6 @@ package org.dspace.content;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
-
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
diff --git a/dspace-api/src/main/java/org/dspace/content/dao/impl/ProcessDAOImpl.java b/dspace-api/src/main/java/org/dspace/content/dao/impl/ProcessDAOImpl.java
index 19070e2957..4c10387d93 100644
--- a/dspace-api/src/main/java/org/dspace/content/dao/impl/ProcessDAOImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/dao/impl/ProcessDAOImpl.java
@@ -8,7 +8,6 @@
package org.dspace.content.dao.impl;
import java.sql.SQLException;
-
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
@@ -21,7 +20,7 @@ import org.dspace.scripts.Process;
import org.dspace.scripts.Process_;
/**
- *
+ *
* Implementation class for {@link ProcessDAO}
*/
public class ProcessDAOImpl extends AbstractHibernateDAO implements ProcessDAO {
diff --git a/dspace-api/src/main/java/org/dspace/content/service/BitstreamService.java b/dspace-api/src/main/java/org/dspace/content/service/BitstreamService.java
index 4250041b5d..b7fe2dfa16 100644
--- a/dspace-api/src/main/java/org/dspace/content/service/BitstreamService.java
+++ b/dspace-api/src/main/java/org/dspace/content/service/BitstreamService.java
@@ -13,7 +13,6 @@ import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
-
import javax.annotation.Nullable;
import org.dspace.authorize.AuthorizeException;
@@ -46,7 +45,7 @@ public interface BitstreamService extends DSpaceObjectService, DSpace
* checksum algorithm as same as the given bitstream.
* This allows multiple bitstreams to share the same internal identifier of assets .
* An example of such a use case scenario is versioning.
- *
+ *
* @param context
* DSpace context object
* @param bitstream
diff --git a/dspace-api/src/main/java/org/dspace/core/Context.java b/dspace-api/src/main/java/org/dspace/core/Context.java
index 64e86eef40..ecfc29d29d 100644
--- a/dspace-api/src/main/java/org/dspace/core/Context.java
+++ b/dspace-api/src/main/java/org/dspace/core/Context.java
@@ -58,6 +58,11 @@ public class Context implements AutoCloseable {
*/
private EPerson currentUser;
+ /**
+ * Temporary store when the current user is temporary switched
+ */
+ private EPerson currentUserPreviousState;
+
/**
* Current Locale
*/
@@ -89,6 +94,11 @@ public class Context implements AutoCloseable {
*/
private List specialGroups;
+ /**
+ * Temporary store for the specialGroups when the current user is temporary switched
+ */
+ private List specialGroupsPreviousState;
+
/**
* Content events
*/
@@ -628,6 +638,42 @@ public class Context implements AutoCloseable {
return myGroups;
}
+ /**
+ * Temporary change the user bound to the context, empty the special groups that
+ * are retained to allow subsequent restore
+ *
+ * @param newUser the EPerson to bound to the context
+ *
+ * @throws IllegalStateException if the switch was already performed without be
+ * restored
+ */
+ public void switchContextUser(EPerson newUser) {
+ if (currentUserPreviousState != null) {
+ throw new IllegalStateException(
+ "A previous user is already set, you can only switch back and foreward one time");
+ }
+
+ currentUserPreviousState = currentUser;
+ specialGroupsPreviousState = specialGroups;
+ specialGroups = new ArrayList();
+ currentUser = newUser;
+ }
+
+ /**
+ * Restore the user bound to the context and his special groups
+ *
+ * @throws IllegalStateException if no switch was performed before
+ */
+ public void restoreContextUser() {
+ if (specialGroupsPreviousState == null) {
+ throw new IllegalStateException("No previous state found");
+ }
+ currentUser = currentUserPreviousState;
+ specialGroups = specialGroupsPreviousState;
+ specialGroupsPreviousState = null;
+ currentUserPreviousState = null;
+ }
+
/**
* Close the context, aborting any open transactions (if any).
* @throws Throwable
diff --git a/dspace-api/src/main/java/org/dspace/core/Utils.java b/dspace-api/src/main/java/org/dspace/core/Utils.java
index 29ed446f1b..18daa5a69e 100644
--- a/dspace-api/src/main/java/org/dspace/core/Utils.java
+++ b/dspace-api/src/main/java/org/dspace/core/Utils.java
@@ -13,8 +13,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
+import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.net.URL;
import java.rmi.dgc.VMID;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -414,6 +416,23 @@ public final class Utils {
}
}
+ /**
+ * Retrieve the baseurl from a given URL string
+ * @param urlString URL string
+ * @return baseurl (without any context path) or null (if URL was invalid)
+ */
+ public static String getBaseUrl(String urlString) {
+ try {
+ URL url = new URL(urlString);
+ String baseUrl = url.getProtocol() + "://" + url.getHost();
+ if (url.getPort() != -1) {
+ baseUrl += (":" + url.getPort());
+ }
+ return baseUrl;
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ }
/**
* Retrieve the hostname from a given URI string
diff --git a/dspace-api/src/main/java/org/dspace/discovery/IndexableObject.java b/dspace-api/src/main/java/org/dspace/discovery/IndexableObject.java
index d201ef8d91..34ad2162c1 100644
--- a/dspace-api/src/main/java/org/dspace/discovery/IndexableObject.java
+++ b/dspace-api/src/main/java/org/dspace/discovery/IndexableObject.java
@@ -21,7 +21,7 @@ import org.dspace.core.ReloadableEntity;
* @param
* the Class of the primary key
*/
-public interface IndexableObject {
+public interface IndexableObject, PK extends Serializable> {
/**
*
diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceParentObjectIndexingPlugin.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceParentObjectIndexingPlugin.java
new file mode 100644
index 0000000000..e4c3e0afcd
--- /dev/null
+++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceParentObjectIndexingPlugin.java
@@ -0,0 +1,44 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.discovery;
+
+import java.sql.SQLException;
+
+import org.apache.solr.common.SolrInputDocument;
+import org.dspace.content.Collection;
+import org.dspace.content.Community;
+import org.dspace.content.DSpaceObject;
+import org.dspace.content.Item;
+import org.dspace.content.factory.ContentServiceFactory;
+import org.dspace.core.Context;
+import org.dspace.discovery.indexobject.IndexableDSpaceObject;
+/**
+ * Indexes the UUID of the parent object for any Community, Collection and Item
+ *
+ * @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
+ */
+public class SolrServiceParentObjectIndexingPlugin implements SolrServiceIndexPlugin {
+
+ @Override
+ public void additionalIndex(Context context, IndexableObject idxObj, SolrInputDocument document) {
+ try {
+ if (idxObj instanceof IndexableDSpaceObject) {
+ DSpaceObject dso = ((IndexableDSpaceObject) idxObj).getIndexedObject();
+ if (dso instanceof Community || dso instanceof Collection || dso instanceof Item) {
+ DSpaceObject parent = ContentServiceFactory.getInstance().getDSpaceObjectService(dso)
+ .getParentObject(context, dso);
+ if (parent != null) {
+ document.addField("location.parent", parent.getID().toString());
+ }
+ }
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+}
diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceResourceRestrictionPlugin.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceResourceRestrictionPlugin.java
index 38e85dbf93..659a3d77d7 100644
--- a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceResourceRestrictionPlugin.java
+++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceResourceRestrictionPlugin.java
@@ -18,7 +18,11 @@ import org.apache.solr.common.SolrInputDocument;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.authorize.service.ResourcePolicyService;
+import org.dspace.content.Collection;
+import org.dspace.content.Community;
import org.dspace.content.DSpaceObject;
+import org.dspace.content.Item;
+import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.CommunityService;
import org.dspace.core.Constants;
@@ -77,6 +81,29 @@ public class SolrServiceResourceRestrictionPlugin implements SolrServiceIndexPlu
//remove the policy from the cache to save memory
context.uncacheEntity(resourcePolicy);
}
+ // also index ADMIN policies as ADMIN permissions provides READ access
+ // going up through the hierarchy for communities, collections and items
+ while (dso != null) {
+ if (dso instanceof Community || dso instanceof Collection || dso instanceof Item) {
+ List policiesAdmin = authorizeService
+ .getPoliciesActionFilter(context, dso, Constants.ADMIN);
+ for (ResourcePolicy resourcePolicy : policiesAdmin) {
+ String fieldValue;
+ if (resourcePolicy.getGroup() != null) {
+ // We have a group add it to the value
+ fieldValue = "g" + resourcePolicy.getGroup().getID();
+ } else {
+ // We have an eperson add it to the value
+ fieldValue = "e" + resourcePolicy.getEPerson().getID();
+ }
+ document.addField("read", fieldValue);
+
+ // remove the policy from the cache to save memory
+ context.uncacheEntity(resourcePolicy);
+ }
+ }
+ dso = ContentServiceFactory.getInstance().getDSpaceObjectService(dso).getParentObject(context, dso);
+ }
} catch (SQLException e) {
log.error(LogManager.getHeader(context, "Error while indexing resource policies",
"DSpace object: (id " + dso.getID() + " type " + dso.getType() + ")"));
diff --git a/dspace-api/src/main/java/org/dspace/handle/HandlePlugin.java b/dspace-api/src/main/java/org/dspace/handle/HandlePlugin.java
index feff8fbda7..3e219b2c34 100644
--- a/dspace-api/src/main/java/org/dspace/handle/HandlePlugin.java
+++ b/dspace-api/src/main/java/org/dspace/handle/HandlePlugin.java
@@ -21,7 +21,6 @@ import net.handle.hdllib.HandleStorage;
import net.handle.hdllib.HandleValue;
import net.handle.hdllib.ScanCallback;
import net.handle.hdllib.Util;
-
import org.apache.logging.log4j.Logger;
import org.dspace.core.Context;
import org.dspace.handle.factory.HandleServiceFactory;
diff --git a/dspace-api/src/main/java/org/dspace/storage/bitstore/BitstreamStorageServiceImpl.java b/dspace-api/src/main/java/org/dspace/storage/bitstore/BitstreamStorageServiceImpl.java
index a0d9101fb8..de1f671ca5 100644
--- a/dspace-api/src/main/java/org/dspace/storage/bitstore/BitstreamStorageServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/storage/bitstore/BitstreamStorageServiceImpl.java
@@ -15,7 +15,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
-
import javax.annotation.Nullable;
import org.apache.commons.collections4.MapUtils;
diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java
index 1ee8f21461..c26864417d 100644
--- a/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/XmlWorkflowServiceImpl.java
@@ -18,7 +18,6 @@ import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.UUID;
-
import javax.mail.MessagingException;
import javax.servlet.http.HttpServletRequest;
diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ReviewAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ReviewAction.java
index 474945048a..5630087d57 100644
--- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ReviewAction.java
+++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ReviewAction.java
@@ -11,7 +11,6 @@ import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
-
import javax.servlet.http.HttpServletRequest;
import org.dspace.app.util.Util;
diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/storedcomponents/dao/impl/XmlWorkflowItemDAOImpl.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/storedcomponents/dao/impl/XmlWorkflowItemDAOImpl.java
index cb6a79e4e0..51728af7a4 100644
--- a/dspace-api/src/main/java/org/dspace/xmlworkflow/storedcomponents/dao/impl/XmlWorkflowItemDAOImpl.java
+++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/storedcomponents/dao/impl/XmlWorkflowItemDAOImpl.java
@@ -9,7 +9,6 @@ package org.dspace.xmlworkflow.storedcomponents.dao.impl;
import java.sql.SQLException;
import java.util.List;
-
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
diff --git a/dspace-api/src/test/java/org/dspace/AbstractIntegrationTest.java b/dspace-api/src/test/java/org/dspace/AbstractIntegrationTest.java
index e6e8addb91..d437a77385 100644
--- a/dspace-api/src/test/java/org/dspace/AbstractIntegrationTest.java
+++ b/dspace-api/src/test/java/org/dspace/AbstractIntegrationTest.java
@@ -7,6 +7,17 @@
*/
package org.dspace;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.nio.channels.FileChannel;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+
+import org.dspace.utils.DSpace;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Ignore;
/**
@@ -21,5 +32,102 @@ import org.junit.Ignore;
*/
@Ignore
public class AbstractIntegrationTest extends AbstractUnitTest {
- // This class intentionally left blank.
+
+ /**
+ * this is the amount of time that guarantee us that changes to the configuration files are picked up.
+ * The actual refresh rate is defined in dspace/config/config-definition.xml
+ */
+ private static final int CONFIG_RELOAD_TIME = 5500;
+
+ /**
+ * holds the size of the local.cfg file, see {@link #cleanExtraConfigurations()}
+ **/
+ private long initialLocalCfgSize;
+
+ /**
+ * set to true if the local cfg has been manipulated
+ */
+ private boolean localCfgChanged = false;
+
+ @Override
+ @Before
+ /**
+ * Extend the {@link AbstractUnitTest#init} method to deal with extra
+ * configuration that can be manipulated at runtime during the Integration Test
+ */
+ public void init() {
+ super.init();
+ String extraConfPath = getLocalConfigurationFilePath();
+ FileChannel fileOpen;
+ try {
+ fileOpen = FileChannel.open(Paths.get(extraConfPath), StandardOpenOption.READ);
+ initialLocalCfgSize = fileOpen.size();
+ fileOpen.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ @After
+ /**
+ * Extend the {@link AbstractUnitTest#destroy} method to deal with extra
+ * configurations that can be manipulated at runtime during the Integration Test
+ */
+ public void destroy() {
+ super.destroy();
+ cleanExtraConfigurations();
+ }
+
+ /**
+ * Restore the local.cfg file to its initial size
+ */
+ protected void cleanExtraConfigurations() {
+ if (!localCfgChanged) {
+ // return immediately as no changes have been applied so we can avoid to wait
+ // for configuration reload
+ return;
+ }
+ String extraConfPath = getLocalConfigurationFilePath();
+ try {
+ FileChannel.open(Paths.get(extraConfPath), StandardOpenOption.WRITE)
+ .truncate(initialLocalCfgSize).close();
+ localCfgChanged = false;
+ // sleep to give the time to the configuration to note the change
+ Thread.sleep(CONFIG_RELOAD_TIME);
+ } catch (IOException | InterruptedException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ *
+ * @return the full path to the in use local.cfg file
+ */
+ private String getLocalConfigurationFilePath() {
+ return new DSpace().getConfigurationService()
+ .getProperty("dspace.dir") + "/config/local.cfg";
+ }
+
+ /**
+ * Append the input text to the current local.cfg file assuring
+ * that the new text goes in a new line and sleep enough time to allow the
+ * configuration reload
+ *
+ * @param textToAppend
+ */
+ protected void appendToLocalConfiguration(String textToAppend) {
+ String extraConfPath = getLocalConfigurationFilePath();
+ try (Writer output = new BufferedWriter(new FileWriter(extraConfPath, true))) {
+ output.append("\n");
+ output.append(textToAppend);
+ output.flush();
+ output.close();
+ localCfgChanged = true;
+ // sleep to give the time to the configuration to note the change
+ Thread.sleep(CONFIG_RELOAD_TIME);
+ } catch (IOException | InterruptedException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
}
diff --git a/dspace-api/src/test/java/org/dspace/authorize/AuthorizeConfigIntegrationTest.java b/dspace-api/src/test/java/org/dspace/authorize/AuthorizeConfigIntegrationTest.java
new file mode 100644
index 0000000000..d338bc6e2c
--- /dev/null
+++ b/dspace-api/src/test/java/org/dspace/authorize/AuthorizeConfigIntegrationTest.java
@@ -0,0 +1,61 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.authorize;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.dspace.AbstractIntegrationTest;
+import org.junit.Test;
+
+/**
+ * This integration test verify that the {@link AuthorizeConfiguration} works
+ * properly with the configuration reloading
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ *
+ */
+public class AuthorizeConfigIntegrationTest extends AbstractIntegrationTest {
+
+ @Test
+ public void testReloadConfiguration() {
+ cleanExtraConfigurations();
+ // the default configuration is to allow delegation of all feature, just check a selection of three permissions
+ assertTrue(AuthorizeConfiguration.canCommunityAdminPerformGroupCreation());
+ assertTrue(AuthorizeConfiguration.canCommunityAdminPerformSubelementDeletion());
+ assertTrue(AuthorizeConfiguration.canCommunityAdminPerformSubelementCreation());
+
+ // in our extra configuration file for test, disable some feature
+ appendToLocalConfiguration(
+ "core.authorization.community-admin.group = false\n" +
+ "core.authorization.community-admin.delete-subelement = false\n");
+ // verify that the two changed one are reflected in the AuthorizationConfiguration
+ assertFalse(AuthorizeConfiguration.canCommunityAdminPerformGroupCreation());
+ assertFalse(AuthorizeConfiguration.canCommunityAdminPerformSubelementDeletion());
+
+ // verify that the third still retain the original value
+ assertTrue(AuthorizeConfiguration.canCommunityAdminPerformSubelementCreation());
+
+ // add other configuration to switch off also the third
+ appendToLocalConfiguration(
+ "core.authorization.community-admin.create-subelement = false\n");
+
+ assertFalse(AuthorizeConfiguration.canCommunityAdminPerformSubelementCreation());
+ assertFalse(AuthorizeConfiguration.canCommunityAdminPerformGroupCreation());
+ assertFalse(AuthorizeConfiguration.canCommunityAdminPerformSubelementDeletion());
+
+ // empty our extra configuration
+ cleanExtraConfigurations();
+
+ // now the default should be returned again
+ assertTrue(AuthorizeConfiguration.canCommunityAdminPerformGroupCreation());
+ assertTrue(AuthorizeConfiguration.canCommunityAdminPerformSubelementCreation());
+ assertTrue(AuthorizeConfiguration.canCommunityAdminPerformSubelementDeletion());
+ }
+
+}
diff --git a/dspace-api/src/test/java/org/dspace/core/UtilsTest.java b/dspace-api/src/test/java/org/dspace/core/UtilsTest.java
index 915c96e209..454cbd8e67 100644
--- a/dspace-api/src/test/java/org/dspace/core/UtilsTest.java
+++ b/dspace-api/src/test/java/org/dspace/core/UtilsTest.java
@@ -22,6 +22,33 @@ import org.junit.Test;
*/
public class UtilsTest extends AbstractUnitTest {
+ /**
+ * Test of getBaseUrl method, of class Utils
+ */
+ @Test
+ public void testGetBaseUrl() {
+ assertEquals("Test remove /server", "http://dspace.org",
+ Utils.getBaseUrl("http://dspace.org/server"));
+
+ assertEquals("Test remove /server/api/core/items", "https://dspace.org",
+ Utils.getBaseUrl("https://dspace.org/server/api/core/items"));
+
+ assertEquals("Test remove trailing slash", "https://dspace.org",
+ Utils.getBaseUrl("https://dspace.org/"));
+
+ assertEquals("Test keep url", "https://demo.dspace.org",
+ Utils.getBaseUrl("https://demo.dspace.org"));
+
+ assertEquals("Test keep url", "http://localhost:8080",
+ Utils.getBaseUrl("http://localhost:8080"));
+
+ assertEquals("Test keep url", "http://localhost:8080",
+ Utils.getBaseUrl("http://localhost:8080/server"));
+
+ // This uses a bunch of reserved URI characters
+ assertNull("Test invalid URI returns null", Utils.getBaseUrl("&+,?/@="));
+ }
+
/**
* Test of getHostName method, of class Utils
*/
diff --git a/dspace-api/src/test/java/org/dspace/xmlworkflow/XmlWorkflowFactoryTest.java b/dspace-api/src/test/java/org/dspace/xmlworkflow/XmlWorkflowFactoryTest.java
index 676285a2b2..a19e6a2622 100644
--- a/dspace-api/src/test/java/org/dspace/xmlworkflow/XmlWorkflowFactoryTest.java
+++ b/dspace-api/src/test/java/org/dspace/xmlworkflow/XmlWorkflowFactoryTest.java
@@ -13,7 +13,6 @@ import static org.junit.Assert.fail;
import java.sql.SQLException;
import org.apache.logging.log4j.Logger;
-
import org.dspace.AbstractUnitTest;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Collection;
@@ -24,7 +23,6 @@ import org.dspace.content.service.CommunityService;
import org.dspace.utils.DSpace;
import org.dspace.xmlworkflow.factory.XmlWorkflowFactory;
import org.dspace.xmlworkflow.state.Workflow;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
diff --git a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java
index 0bf2fef557..79ee64ded5 100644
--- a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java
+++ b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java
@@ -22,7 +22,6 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
-
import javax.xml.stream.XMLStreamException;
import com.lyncode.xoai.dataprovider.exceptions.ConfigurationException;
diff --git a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/DSpaceEarliestDateResolver.java b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/DSpaceEarliestDateResolver.java
index ba51c62d1b..6c378ff6d1 100644
--- a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/DSpaceEarliestDateResolver.java
+++ b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/DSpaceEarliestDateResolver.java
@@ -9,7 +9,6 @@ package org.dspace.xoai.services.impl;
import java.sql.SQLException;
import java.util.Date;
-
import javax.persistence.NoResultException;
import org.apache.logging.log4j.LogManager;
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java
index 406bf1aec5..68f9085e21 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java
@@ -10,6 +10,7 @@ package org.dspace.app.rest;
import java.sql.SQLException;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.converter.EPersonConverter;
@@ -20,6 +21,7 @@ import org.dspace.app.rest.model.EPersonRest;
import org.dspace.app.rest.model.hateoas.AuthenticationStatusResource;
import org.dspace.app.rest.model.hateoas.AuthnResource;
import org.dspace.app.rest.projection.Projection;
+import org.dspace.app.rest.security.RestAuthenticationService;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.Utils;
import org.dspace.core.Context;
@@ -60,6 +62,9 @@ public class AuthenticationRestController implements InitializingBean {
@Autowired
private HalLinkService halLinkService;
+ @Autowired
+ private RestAuthenticationService restAuthenticationService;
+
@Autowired
private Utils utils;
@@ -77,7 +82,8 @@ public class AuthenticationRestController implements InitializingBean {
}
@RequestMapping(value = "/status", method = RequestMethod.GET)
- public AuthenticationStatusResource status(HttpServletRequest request) throws SQLException {
+ public AuthenticationStatusResource status(HttpServletRequest request, HttpServletResponse response)
+ throws SQLException {
Context context = ContextUtil.obtainContext(request);
EPersonRest ePersonRest = null;
Projection projection = utils.obtainProjection();
@@ -86,6 +92,14 @@ public class AuthenticationRestController implements InitializingBean {
}
AuthenticationStatusRest authenticationStatusRest = new AuthenticationStatusRest(ePersonRest);
+ // Whether authentication status is false add WWW-Authenticate so client can retrieve the available
+ // authentication methods
+ if (!authenticationStatusRest.isAuthenticated()) {
+ String authenticateHeaderValue = restAuthenticationService
+ .getWwwAuthenticateHeaderValue(request, response);
+
+ response.setHeader("WWW-Authenticate", authenticateHeaderValue);
+ }
authenticationStatusRest.setProjection(projection);
AuthenticationStatusResource authenticationStatusResource = converter.toResource(authenticationStatusRest);
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/CommunityLogoController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/CommunityLogoController.java
index 78b59730c8..baf8009006 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/CommunityLogoController.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/CommunityLogoController.java
@@ -10,7 +10,6 @@ package org.dspace.app.rest;
import java.io.IOException;
import java.sql.SQLException;
import java.util.UUID;
-
import javax.servlet.http.HttpServletRequest;
import org.dspace.app.rest.exception.UnprocessableEntityException;
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java
index fd123acf15..42ad173f2e 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java
@@ -12,7 +12,6 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java
index bca634cb7d..ab338af966 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java
@@ -36,6 +36,7 @@ import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.converter.JsonPatchConverter;
import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.exception.PaginationException;
+import org.dspace.app.rest.exception.RESTAuthorizationException;
import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
import org.dspace.app.rest.exception.RepositoryNotFoundException;
import org.dspace.app.rest.exception.RepositorySearchMethodNotFoundException;
@@ -583,9 +584,11 @@ public class RestResourceController implements InitializingBean {
RestAddressableModel modelObject = null;
try {
modelObject = repository.upload(request, apiCategory, model, id, uploadfile);
- } catch (SQLException e) {
- log.error(e.getMessage(), e);
- return ControllerUtils.toEmptyResponse(HttpStatus.INTERNAL_SERVER_ERROR);
+ } catch (SQLException | IOException e) {
+ throw new RuntimeException("Error " + e.getMessage() +
+ " uploading file to " + model + " with ID= " + id, e);
+ } catch ( AuthorizeException ae) {
+ throw new RESTAuthorizationException(ae);
}
DSpaceResource result = converter.toResource(modelObject);
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, new HttpHeaders(), result);
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ShibbolethRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ShibbolethRestController.java
new file mode 100644
index 0000000000..7355bab2a8
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ShibbolethRestController.java
@@ -0,0 +1,60 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest;
+
+import java.io.IOException;
+import java.util.Arrays;
+import javax.servlet.http.HttpServletResponse;
+
+import org.dspace.app.rest.model.AuthnRest;
+import org.dspace.services.ConfigurationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.hateoas.Link;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Rest controller that handles redirect after shibboleth authentication succeded
+ *
+ * @author Andrea Bollini (andrea dot bollini at 4science dot it)
+ * @author Giuseppe Digilio (giuseppe dot digilio at 4science dot it)
+ */
+@RequestMapping(value = "/api/" + AuthnRest.CATEGORY + "/shibboleth")
+@RestController
+public class ShibbolethRestController implements InitializingBean {
+
+ private static final Logger log = LoggerFactory.getLogger(ShibbolethRestController.class);
+
+ @Autowired
+ ConfigurationService configurationService;
+
+ @Autowired
+ DiscoverableEndpointsService discoverableEndpointsService;
+
+ @Override
+ public void afterPropertiesSet() {
+ discoverableEndpointsService
+ .register(this, Arrays.asList(new Link("/api/" + AuthnRest.CATEGORY, "shibboleth")));
+ }
+
+ @RequestMapping(method = RequestMethod.GET)
+ public void shibboleth(HttpServletResponse response,
+ @RequestParam(name = "redirectUrl", required = false) String redirectUrl) throws IOException {
+ if (redirectUrl == null) {
+ redirectUrl = configurationService.getProperty("dspace.ui.url");
+ }
+ log.info("Redirecting to " + redirectUrl);
+ response.sendRedirect(redirectUrl);
+ }
+
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/Authorization.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/Authorization.java
new file mode 100644
index 0000000000..60f6ff4ffb
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/Authorization.java
@@ -0,0 +1,122 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.authorization;
+
+import org.dspace.app.rest.model.BaseObjectRest;
+import org.dspace.app.rest.model.RestAddressableModel;
+import org.dspace.content.Site;
+import org.dspace.eperson.EPerson;
+
+/**
+ * An authorization is the right for a specific {@link EPerson}, eventually null to indicate unauthenticated users, to
+ * use a specific {@link AuthorizationFeature} on a defined object. The target object must implement the
+ * {@link RestAddressableModel} interface so to be directly addressable
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+public class Authorization {
+ /**
+ * the user authorized to use the feature. null
mean unauthenticated user
+ */
+ private EPerson eperson;
+
+ /**
+ * the feature that is authorized to be used
+ */
+ private AuthorizationFeature feature;
+
+ /**
+ * the object where the feature can be used. Not null, for repository wide feature use the {@link Site} object
+ */
+ private BaseObjectRest object;
+
+ public Authorization() {
+ }
+
+ public Authorization(EPerson eperson, AuthorizationFeature feature, BaseObjectRest object) {
+ super();
+ this.eperson = eperson;
+ this.feature = feature;
+ this.object = object;
+ }
+
+
+ /**
+ *
+ * @return the user authorized to use the feature. null
mean unauthenticated user
+ */
+ public EPerson getEperson() {
+ return eperson;
+ }
+
+ /**
+ *
+ * @param eperson
+ * the user authorized to use the feature. null
mean unauthenticated user
+ */
+ public void setEperson(EPerson eperson) {
+ this.eperson = eperson;
+ }
+
+ /**
+ *
+ * @return the feature that is authorized to be used
+ */
+ public AuthorizationFeature getFeature() {
+ return feature;
+ }
+
+ /**
+ *
+ * @param feature
+ * the feature that is authorized to be used
+ */
+ public void setFeature(AuthorizationFeature feature) {
+ this.feature = feature;
+ }
+
+ /**
+ *
+ * @return the object where the feature can be used. Not null, for repository wide feature use the {@link Site}
+ * object
+ */
+ public BaseObjectRest getObject() {
+ return object;
+ }
+
+ /**
+ *
+ * @param object
+ * the object where the feature can be used. Not null, for repository wide feature use the {@link Site}
+ * object
+ */
+ public void setObject(BaseObjectRest object) {
+ this.object = object;
+ }
+
+
+ /**
+ *
+ * @return an unique business identifier generated by concatenation of eperson uuid (if any), feature name and
+ * object unique identifier.
+ * Some examples
+ * alwaystrue_core.site_94274020-e617-43b8-90e6-277d04f6be13
+ * 8c7b9132-eadc-4199-af6d-3260a678e96f_alwaystrueadmins_core.site_94274020-e617-43b8-90e6-277d04f6be13
+ * 8c7b9132-eadc-4199-af6d-3260a678e96f_withdrawItem_core.item_c8924526-67ef-479a-8e37-dd8d19e625e9
+ * 8c7b9132-eadc-4199-af6d-3260a678e96f_alwaystrue_submission.workspaceitem_1
+ */
+ public String getID() {
+ StringBuilder sb = new StringBuilder();
+ if (eperson != null) {
+ sb.append(eperson.getID().toString()).append("_");
+ }
+ sb.append(feature.getName()).append("_").append(object.getUniqueType()).append("_").append(object.getId());
+ return sb.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/AuthorizationFeature.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/AuthorizationFeature.java
new file mode 100644
index 0000000000..f2f1ae31f5
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/AuthorizationFeature.java
@@ -0,0 +1,72 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.authorization;
+
+import java.sql.SQLException;
+
+import org.dspace.app.rest.model.BaseObjectRest;
+import org.dspace.app.rest.model.RestAddressableModel;
+import org.dspace.app.rest.model.SiteRest;
+import org.dspace.core.Context;
+import org.springframework.core.annotation.AnnotationUtils;
+
+/**
+ * A feature is the representation of a business goal used in the Authorization endpoint to declare what an user can do
+ * on a specific object.
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+public interface AuthorizationFeature {
+ /**
+ * Check if the eperson in the provided context, or the anonymous user if not loggedin, has access to the feature
+ * for the requested object
+ *
+ * @param context
+ * the DSpace Context
+ * @param object
+ * the object target by the feature (MUST be NOT null). Use the {@link SiteRest} object for repository
+ * wide feature
+ * @return true if the user associated with the context has access to the feature for the specified object
+ */
+ boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException;
+
+ /**
+ * Return the name of the feature
+ *
+ * @return the name of the feature
+ */
+ default String getName() {
+ AuthorizationFeatureDocumentation anno =
+ AnnotationUtils.findAnnotation(this.getClass(), AuthorizationFeatureDocumentation.class);
+ if (anno != null) {
+ return anno.name();
+ }
+ return this.getClass().getSimpleName();
+ }
+
+ /**
+ * Return the description of the feature
+ *
+ * @return the description of the feature
+ */
+ default String getDescription() {
+ AuthorizationFeatureDocumentation anno =
+ AnnotationUtils.findAnnotation(this.getClass(), AuthorizationFeatureDocumentation.class);
+ if (anno != null) {
+ return anno.description();
+ }
+ return this.getClass().getSimpleName();
+ }
+
+ /**
+ * Return the supported object type according to the {@link RestAddressableModel#getUniqueType()}
+ *
+ * @return the supported object type, required to be not null
+ */
+ String[] getSupportedTypes();
+}
\ No newline at end of file
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/AuthorizationFeatureDocumentation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/AuthorizationFeatureDocumentation.java
new file mode 100644
index 0000000000..dc34bef1d0
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/AuthorizationFeatureDocumentation.java
@@ -0,0 +1,39 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.authorization;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Class level annotation to document an {@link AuthorizationFeature}
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface AuthorizationFeatureDocumentation {
+
+ /**
+ * The name of the authorization feature (required).
+ *
+ * @return the name.
+ */
+ String name();
+
+ /**
+ * The description of the authorization feature.
+ *
+ * @return the description of the authorization feature, or the empty string if unspecified by the annotation.
+ */
+ String description() default "";
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/AuthorizationFeatureService.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/AuthorizationFeatureService.java
new file mode 100644
index 0000000000..2b04bb983c
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/AuthorizationFeatureService.java
@@ -0,0 +1,63 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.authorization;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import org.dspace.app.rest.model.BaseObjectRest;
+import org.dspace.app.rest.model.SiteRest;
+import org.dspace.core.Context;
+
+/**
+ * This service provides access to the Authorization Features and check if the feature is allowed or not in a specific
+ * context and object.
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+public interface AuthorizationFeatureService {
+ /**
+ * Check if the eperson in the provided context, or the anonymous user if not loggedin, has access to the requested
+ * feature for the requested object
+ *
+ * @param context
+ * the DSpace Context
+ * @param feature
+ * the Authorization Feature to check
+ * @param object
+ * the object target by the feature. Passing a null object always return false. To check repository wide
+ * feature pass the {@link SiteRest} object
+ * @return true if the user associated with the context has access to the feature
+ */
+ boolean isAuthorized(Context context, AuthorizationFeature feature, BaseObjectRest object) throws SQLException;
+
+ /**
+ * Get all the authorization features defined in the system
+ *
+ * @return a list of all the authorization features
+ */
+ public List findAll();
+
+ /**
+ * Get the authorization feature by its unique name
+ *
+ * @param name
+ * the authorization feature unique name
+ * @return the authorization feature if any
+ */
+ public AuthorizationFeature find(String name);
+
+ /**
+ * Return all the feature that apply to the rest resources identified by the
+ * uniqueType string category.model
+ *
+ * @param categoryDotModel
+ * @return
+ */
+ List findByResourceType(String categoryDotModel);
+}
\ No newline at end of file
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/AuthorizationRestUtil.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/AuthorizationRestUtil.java
new file mode 100644
index 0000000000..c8a9d2435b
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/AuthorizationRestUtil.java
@@ -0,0 +1,149 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.authorization;
+
+import java.io.Serializable;
+import java.sql.SQLException;
+import java.util.UUID;
+
+import org.apache.commons.lang3.StringUtils;
+import org.dspace.app.rest.model.BaseObjectRest;
+import org.dspace.app.rest.repository.DSpaceRestRepository;
+import org.dspace.app.rest.repository.ReloadableEntityObjectRepository;
+import org.dspace.app.rest.utils.Utils;
+import org.dspace.core.Context;
+import org.dspace.eperson.EPerson;
+import org.dspace.eperson.factory.EPersonServiceFactory;
+import org.dspace.eperson.service.EPersonService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * Utility class to manipulate the AuthorizationRest object
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ *
+ */
+@Component
+public class AuthorizationRestUtil {
+
+ @Autowired
+ private Utils utils;
+
+ /**
+ * Extract the feature name from the Authorization business ID. See {@link Authorization#getID()}
+ *
+ * @param id
+ * the Authorization business ID
+ * @return the feature name
+ */
+ public String getFeatureName(String id) {
+ return splitIdParts(id)[1];
+ }
+
+ /**
+ * Get the object addressed in the authorization extracting its type and primary key from the authorization business
+ * ID ({@link Authorization#getID()}) and using the appropriate service
+ *
+ * @param context
+ * the DSpace context
+ * @param id
+ * the Authorization business ID. See {@link Authorization#getID()}
+ * @return the object addressed in the authorization
+ * @throws SQLException
+ * if an error occur retrieving the data from the database
+ * @throws IllegalArgumentException
+ * if the specified id doesn't contain syntactically valid object information
+ */
+ public BaseObjectRest getObject(Context context, String id) throws SQLException {
+ String[] parts = splitIdParts(id);
+ String objIdStr = parts[3];
+ String[] objType;
+ try {
+ objType = parts[2].split("\\.");
+ DSpaceRestRepository repository = utils.getResourceRepositoryByCategoryAndModel(objType[0], objType[1]);
+ Serializable pk = utils.castToPKClass((ReloadableEntityObjectRepository) repository, objIdStr);
+ try {
+ // disable the security as we only need to retrieve the object to further process the authorization
+ context.turnOffAuthorisationSystem();
+ return (BaseObjectRest) repository.findOne(context, pk);
+ } finally {
+ context.restoreAuthSystemState();
+ }
+ } catch (RuntimeException e) {
+ throw new IllegalArgumentException(
+ "The id " + id + " not resolve to a valid object", e);
+ }
+ }
+
+ /**
+ * Get the eperson in the authorization extracting its uuid from the authorization business ID
+ * ({@link Authorization#getID()}) and retrieving the corresponding eperson object with the {@link EPersonService}.
+ * Please note that reference to deleted eperson will result in an IllegalArgumentException
+ *
+ * @param context
+ * the DSpace context
+ * @param id
+ * the Authorization business ID. See {@link Authorization#getID()}
+ * @return the eperson addressed in the authorization or null if not specified.
+ * @throws SQLException
+ * if an error occur retrieving the data from the database
+ * @throws IllegalArgumentException
+ * if the specified id doesn't contain syntactically valid object information
+ */
+ public EPerson getEperson(Context context, String id) throws SQLException {
+ String epersonIdStr = splitIdParts(id)[0];
+ if (StringUtils.isBlank(epersonIdStr)) {
+ return null;
+ }
+ UUID uuid;
+ try {
+ uuid = UUID.fromString(epersonIdStr);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("The authorization id " + id +
+ " contains a reference to an invalid eperson uuid " + epersonIdStr);
+ }
+ EPersonService service = EPersonServiceFactory.getInstance().getEPersonService();
+ EPerson ep = service.find(context, uuid);
+ if (ep == null) {
+ throw new IllegalArgumentException("No eperson found with the uuid " + epersonIdStr);
+ }
+ return ep;
+ }
+
+ /**
+ * Split the business ID in an array with a fixed length (4) as follow eperson uuid, feature name, object type id,
+ * object id
+ *
+ * @param id
+ * the Authorization business ID. See {@link Authorization#getID()}
+ * @return an array with a fixed length (4) as follow eperson uuid, feature name, object type id, object id
+ */
+ private String[] splitIdParts(String id) {
+ String[] idParts = id.split("_");
+ String eperson = null;
+ String feature = null;
+ String objType = null;
+ String objId = null;
+ if (idParts.length == 4) {
+ eperson = idParts[0];
+ feature = idParts[1];
+ objType = idParts[2];
+ objId = idParts[3];
+ } else if (idParts.length == 3) {
+ feature = idParts[0];
+ objType = idParts[1];
+ objId = idParts[2];
+ } else {
+ throw new IllegalArgumentException(
+ "the authoization id is invalid, it must have the form " +
+ "[eperson-uuid_]feature-id_object-type_object-id");
+ }
+ return new String[] { eperson, feature, objType, objId };
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/AuthorizationFeatureServiceImpl.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/AuthorizationFeatureServiceImpl.java
new file mode 100644
index 0000000000..01385d4435
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/AuthorizationFeatureServiceImpl.java
@@ -0,0 +1,78 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.authorization.impl;
+
+import java.sql.SQLException;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.dspace.app.rest.authorization.AuthorizationFeature;
+import org.dspace.app.rest.authorization.AuthorizationFeatureService;
+import org.dspace.app.rest.model.BaseObjectRest;
+import org.dspace.app.rest.utils.Utils;
+import org.dspace.core.Context;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * This is the default implementation of the {@link AuthorizationFeatureService}. It is based on the spring autowiring
+ * feature to discover all the features available in the system
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ *
+ */
+@Service
+public class AuthorizationFeatureServiceImpl implements AuthorizationFeatureService {
+ @Autowired
+ private List features;
+
+ @Autowired
+ private Utils utils;
+
+ @Override
+ public boolean isAuthorized(Context context, AuthorizationFeature feature, BaseObjectRest object)
+ throws SQLException {
+ if (object == null) {
+ // the authorization interface require that the object is not null
+ return false;
+ }
+
+ if (feature == null
+ || !ArrayUtils.contains(feature.getSupportedTypes(), object.getUniqueType())) {
+ return false;
+ }
+
+ return feature.isAuthorized(context, object);
+ }
+
+ @Override
+ public List findAll() {
+ return features;
+ }
+
+ @Override
+ public AuthorizationFeature find(String name) {
+ for (AuthorizationFeature feature : features) {
+ if (StringUtils.equals(name, feature.getName())) {
+ return feature;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public List findByResourceType(String categoryDotType) {
+ // Loops through all features, returning any that match the given categoryDotType
+ return features
+ .stream()
+ .filter(f -> ArrayUtils.contains(f.getSupportedTypes(), categoryDotType))
+ .collect(Collectors.toList());
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/CCLicenseFeature.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/CCLicenseFeature.java
new file mode 100644
index 0000000000..94b8d19d93
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/CCLicenseFeature.java
@@ -0,0 +1,59 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.authorization.impl;
+
+import java.sql.SQLException;
+
+import org.dspace.app.rest.authorization.AuthorizationFeature;
+import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
+import org.dspace.app.rest.model.BaseObjectRest;
+import org.dspace.app.rest.model.ItemRest;
+import org.dspace.app.rest.utils.Utils;
+import org.dspace.app.util.AuthorizeUtil;
+import org.dspace.authorize.AuthorizeException;
+import org.dspace.content.Item;
+import org.dspace.core.Context;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * The cclicense feature. It can be used by administrators (or community/collection delegate) to manage the Creative
+ * Commons license for an item
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@Component
+@AuthorizationFeatureDocumentation(name = CCLicenseFeature.NAME,
+ description = "It can be used by administrators (or community/collection delegate) to manage the Creative " +
+ "Commons license for an item")
+public class CCLicenseFeature implements AuthorizationFeature {
+
+ public static final String NAME = "cclicense";
+
+ @Autowired
+ private Utils utils;
+
+ @Override
+ public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
+ if (!(object instanceof ItemRest)) {
+ return false;
+ }
+ Item item = (Item) utils.getDSpaceAPIObjectFromRest(context, object);
+ try {
+ AuthorizeUtil.authorizeManageCCLicense(context, item);
+ } catch (AuthorizeException e) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String[] getSupportedTypes() {
+ return new String[] { ItemRest.CATEGORY + "." + ItemRest.NAME };
+ }
+}
\ No newline at end of file
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/ReinstateFeature.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/ReinstateFeature.java
new file mode 100644
index 0000000000..796f3a457e
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/ReinstateFeature.java
@@ -0,0 +1,62 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.authorization.impl;
+
+import java.sql.SQLException;
+
+import org.dspace.app.rest.authorization.AuthorizationFeature;
+import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
+import org.dspace.app.rest.model.BaseObjectRest;
+import org.dspace.app.rest.model.ItemRest;
+import org.dspace.app.rest.utils.Utils;
+import org.dspace.app.util.AuthorizeUtil;
+import org.dspace.authorize.AuthorizeException;
+import org.dspace.content.Item;
+import org.dspace.core.Context;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * The reinstate feature. It can be used by administrators (or community/collection delegate) to reinstate an item that
+ * was previously withdrawn
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@Component
+@AuthorizationFeatureDocumentation(name = ReinstateFeature.NAME,
+ description = "It can be used by administrators (or community/collection delegate) to reinstate an item that "
+ + "was previously withdrawn")
+public class ReinstateFeature implements AuthorizationFeature {
+
+ public static final String NAME = "reinstateItem";
+
+ @Autowired
+ private Utils utils;
+
+ @Override
+ public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
+ if (!(object instanceof ItemRest)) {
+ return false;
+ }
+ Item item = (Item) utils.getDSpaceAPIObjectFromRest(context, object);
+ if (!item.isWithdrawn()) {
+ return false;
+ }
+ try {
+ AuthorizeUtil.authorizeReinstateItem(context, item);
+ } catch (AuthorizeException e) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String[] getSupportedTypes() {
+ return new String[] { ItemRest.CATEGORY + "." + ItemRest.NAME };
+ }
+}
\ No newline at end of file
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/WithdrawFeature.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/WithdrawFeature.java
new file mode 100644
index 0000000000..c6f135c496
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/WithdrawFeature.java
@@ -0,0 +1,62 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.authorization.impl;
+
+import java.sql.SQLException;
+
+import org.dspace.app.rest.authorization.AuthorizationFeature;
+import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
+import org.dspace.app.rest.model.BaseObjectRest;
+import org.dspace.app.rest.model.ItemRest;
+import org.dspace.app.rest.utils.Utils;
+import org.dspace.app.util.AuthorizeUtil;
+import org.dspace.authorize.AuthorizeException;
+import org.dspace.content.Item;
+import org.dspace.core.Context;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * The withdrawn feature. It can be used by administrators (or community/collection delegate) to logically delete an
+ * item retiring it from the archive
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@Component
+@AuthorizationFeatureDocumentation(name = WithdrawFeature.NAME,
+ description = "It can be used by administrators (or community/collection delegate) to logically delete an "
+ + "item retiring it from the archive")
+public class WithdrawFeature implements AuthorizationFeature {
+
+ public final static String NAME = "withdrawItem";
+
+ @Autowired
+ private Utils utils;
+
+ @Override
+ public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
+ if (!(object instanceof ItemRest)) {
+ return false;
+ }
+ Item item = (Item) utils.getDSpaceAPIObjectFromRest(context, object);
+ if (!item.isArchived()) {
+ return false;
+ }
+ try {
+ AuthorizeUtil.authorizeWithdrawItem(context, item);
+ } catch (AuthorizeException e) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String[] getSupportedTypes() {
+ return new String[] { ItemRest.CATEGORY + "." + ItemRest.NAME };
+ }
+}
\ No newline at end of file
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AuthorizationConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AuthorizationConverter.java
new file mode 100644
index 0000000000..0179243303
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AuthorizationConverter.java
@@ -0,0 +1,40 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.converter;
+
+import org.dspace.app.rest.authorization.Authorization;
+import org.dspace.app.rest.model.AuthorizationRest;
+import org.dspace.app.rest.projection.Projection;
+import org.springframework.stereotype.Component;
+
+/**
+ * This class provides the method to convert an Authorization to its REST representation, the
+ * AuthorizationRest
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@Component
+public class AuthorizationConverter
+ implements DSpaceConverter {
+
+ @Override
+ public AuthorizationRest convert(Authorization authz, Projection projection) {
+ AuthorizationRest featureRest = new AuthorizationRest();
+ featureRest.setProjection(projection);
+ if (authz != null) {
+ featureRest.setId(authz.getID());
+ }
+ return featureRest;
+ }
+
+ @Override
+ public Class getModelClass() {
+ return Authorization.class;
+ }
+
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AuthorizationFeatureConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AuthorizationFeatureConverter.java
new file mode 100644
index 0000000000..25dd2167be
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/AuthorizationFeatureConverter.java
@@ -0,0 +1,49 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.converter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.dspace.app.rest.authorization.AuthorizationFeature;
+import org.dspace.app.rest.model.AuthorizationFeatureRest;
+import org.dspace.app.rest.projection.Projection;
+import org.springframework.stereotype.Component;
+
+/**
+ * This class provides the method to convert an AuthorizationFeature to its REST representation, the
+ * AuthorizationFeatureRest
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@Component
+public class AuthorizationFeatureConverter
+ implements DSpaceConverter {
+
+ @Override
+ public AuthorizationFeatureRest convert(AuthorizationFeature feature, Projection projection) {
+ AuthorizationFeatureRest featureRest = new AuthorizationFeatureRest();
+ featureRest.setProjection(projection);
+ if (feature != null) {
+ featureRest.setId(feature.getName());
+ featureRest.setDescription(feature.getDescription());
+ List types = new ArrayList();
+ for (String t : feature.getSupportedTypes()) {
+ types.add(t);
+ }
+ featureRest.setResourceTypes(types);
+ }
+ return featureRest;
+ }
+
+ @Override
+ public Class getModelClass() {
+ return AuthorizationFeature.class;
+ }
+
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ScriptConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ScriptConverter.java
index a307974ee9..379d651617 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ScriptConverter.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ScriptConverter.java
@@ -38,7 +38,9 @@ public class ScriptConverter implements DSpaceConverter {
+
+ private static final Logger log = Logger.getLogger(VersionConverter.class);
+
+ @Autowired
+ private AuthorizeService authorizeService;
+
+ @Autowired
+ private ConfigurationService configurationService;
+
+ @Autowired
+ private RequestService requestService;
+
+ @Override
+ public VersionRest convert(Version modelObject, Projection projection) {
+ VersionRest versionRest = new VersionRest();
+ versionRest.setCreated(modelObject.getVersionDate());
+ versionRest.setId(modelObject.getID());
+ versionRest.setSummary(modelObject.getSummary());
+ setSubmitterName(modelObject, versionRest);
+ versionRest.setVersion(modelObject.getVersionNumber());
+ versionRest.setProjection(projection);
+ return versionRest;
+ }
+
+ private void setSubmitterName(Version modelObject, VersionRest versionRest) {
+ Context context = null;
+ Request currentRequest = requestService.getCurrentRequest();
+ if (currentRequest != null) {
+ context = ContextUtil.obtainContext(currentRequest.getHttpServletRequest());
+ }
+ try {
+ if ((context != null && authorizeService.isAdmin(context)) || configurationService
+ .getBooleanProperty("versioning.item.history.include.submitter")) {
+ EPerson submitter = modelObject.getEPerson();
+ if (submitter != null) {
+ versionRest.setSubmitterName(submitter.getFullName());
+ }
+ }
+ } catch (SQLException e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public Class getModelClass() {
+ return Version.class;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/VersionHistoryConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/VersionHistoryConverter.java
new file mode 100644
index 0000000000..1b14744132
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/VersionHistoryConverter.java
@@ -0,0 +1,32 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.converter;
+
+import org.dspace.app.rest.model.VersionHistoryRest;
+import org.dspace.app.rest.projection.Projection;
+import org.dspace.versioning.VersionHistory;
+import org.springframework.stereotype.Component;
+
+/**
+ * This is the Converter that takes care of the conversion between {@link VersionHistory} and {@link VersionHistoryRest}
+ */
+@Component
+public class VersionHistoryConverter implements DSpaceConverter {
+
+ @Override
+ public VersionHistoryRest convert(VersionHistory modelObject, Projection projection) {
+ VersionHistoryRest versionHistoryRest = new VersionHistoryRest();
+ versionHistoryRest.setId(modelObject.getID());
+ return versionHistoryRest;
+ }
+
+ @Override
+ public Class getModelClass() {
+ return VersionHistory.class;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/AuthnHalLinkFactory.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/AuthnHalLinkFactory.java
index 4505ab3404..377acda1bc 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/AuthnHalLinkFactory.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/AuthnHalLinkFactory.java
@@ -35,7 +35,7 @@ public class AuthnHalLinkFactory extends HalLinkFactory {
+ public static final String NAME = "feature";
+ public static final String CATEGORY = RestAddressableModel.AUTHORIZATION;
+
+ private String description;
+
+ @JsonProperty(value = "resourcetypes")
+ private List resourceTypes;
+
+ @Override
+ @JsonProperty(access = Access.READ_ONLY)
+ public String getType() {
+ return NAME;
+ }
+
+ @Override
+ public String getCategory() {
+ return CATEGORY;
+ }
+
+ @Override
+ public Class getController() {
+ return RestResourceController.class;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public List getResourceTypes() {
+ return resourceTypes;
+ }
+
+ public void setResourceTypes(List resourceTypes) {
+ this.resourceTypes = resourceTypes;
+ }
+
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AuthorizationRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AuthorizationRest.java
new file mode 100644
index 0000000000..95f2888313
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AuthorizationRest.java
@@ -0,0 +1,49 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonProperty.Access;
+import org.dspace.app.rest.RestResourceController;
+
+/**
+ * The Authorization REST Resource. An authorization is the representation of some rights that are available to a
+ * specific user (eperson) on a defined object, eventually the whole repository (site object).
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@LinksRest(links = {
+ @LinkRest(method = "getEperson", name = AuthorizationRest.EPERSON),
+ @LinkRest(method = "getFeature", name = AuthorizationRest.FEATURE),
+ @LinkRest(method = "getObject", name = AuthorizationRest.OBJECT)
+})
+public class AuthorizationRest extends BaseObjectRest {
+ public static final String NAME = "authorization";
+ public static final String CATEGORY = RestAddressableModel.AUTHORIZATION;
+
+ public static final String EPERSON = "eperson";
+ public static final String FEATURE = "feature";
+ public static final String OBJECT = "object";
+
+ @Override
+ @JsonProperty(access = Access.READ_ONLY)
+ public String getType() {
+ return NAME;
+ }
+
+ @Override
+ public String getCategory() {
+ return CATEGORY;
+ }
+
+ @Override
+ public Class getController() {
+ return RestResourceController.class;
+ }
+
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java
index b63fa4779a..4b7229cab7 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java
@@ -33,6 +33,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
name = ItemRest.RELATIONSHIPS,
method = "getRelationships"
),
+ @LinkRest(
+ name = ItemRest.VERSION,
+ method = "getItemVersion"
+ ),
@LinkRest(
name = ItemRest.TEMPLATE_ITEM_OF,
method = "getTemplateItemOf"
@@ -47,6 +51,7 @@ public class ItemRest extends DSpaceObjectRest {
public static final String MAPPED_COLLECTIONS = "mappedCollections";
public static final String OWNING_COLLECTION = "owningCollection";
public static final String RELATIONSHIPS = "relationships";
+ public static final String VERSION = "version";
public static final String TEMPLATE_ITEM_OF = "templateItemOf";
private boolean inArchive = false;
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ParameterRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ParameterRest.java
index b24544c1bc..53eb2033b9 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ParameterRest.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ParameterRest.java
@@ -25,6 +25,16 @@ public class ParameterRest {
*/
private String type;
+ /**
+ * The long name of the parameter
+ */
+ private String nameLong;
+
+ /**
+ * Boolean indicating whether the parameter is mandatory or not
+ */
+ private boolean mandatory;
+
public String getName() {
return name;
}
@@ -48,4 +58,36 @@ public class ParameterRest {
public void setType(String type) {
this.type = type;
}
+
+ /**
+ * Generic getter for the nameLong
+ * @return the nameLong value of this ParameterRest
+ */
+ public String getNameLong() {
+ return nameLong;
+ }
+
+ /**
+ * Generic setter for the nameLong
+ * @param nameLong The nameLong to be set on this ParameterRest
+ */
+ public void setNameLong(String nameLong) {
+ this.nameLong = nameLong;
+ }
+
+ /**
+ * Generic getter for the mandatory
+ * @return the mandatory value of this ParameterRest
+ */
+ public boolean isMandatory() {
+ return mandatory;
+ }
+
+ /**
+ * Generic setter for the mandatory
+ * @param mandatory The mandatory to be set on this ParameterRest
+ */
+ public void setMandatory(boolean mandatory) {
+ this.mandatory = mandatory;
+ }
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RestAddressableModel.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RestAddressableModel.java
index 3b9988bfce..2740d428f7 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RestAddressableModel.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RestAddressableModel.java
@@ -44,4 +44,16 @@ public abstract class RestAddressableModel implements RestModel {
public void setProjection(Projection projection) {
this.projection = projection;
}
+
+ @JsonIgnore
+ /**
+ *
+ * @return an unique string useful to unambiguously identify the type of rest
+ * object. It is in the form of category.model where model is in its
+ * singular form. Examples include core.item, core.community,
+ * submission.workspaceitem, etc.
+ */
+ public String getUniqueType() {
+ return getCategory() + "." + getType();
+ }
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RestModel.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RestModel.java
index 39b84f2ee7..940f4a4deb 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RestModel.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RestModel.java
@@ -31,6 +31,7 @@ public interface RestModel extends Serializable {
public static final String SYSTEM = "system";
public static final String WORKFLOW = "workflow";
public static final String AUTHORIZATION = "authz";
+ public static final String VERSIONING = "versioning";
public String getType();
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionHistoryRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionHistoryRest.java
new file mode 100644
index 0000000000..eddca157a4
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionHistoryRest.java
@@ -0,0 +1,61 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.model;
+
+import org.dspace.app.rest.RestResourceController;
+
+/**
+ * The REST object for the {@link org.dspace.versioning.VersionHistory} object
+ */
+@LinksRest(links = {
+ @LinkRest(
+ name = VersionHistoryRest.VERSIONS,
+ method = "getVersions"
+ )
+})
+public class VersionHistoryRest extends BaseObjectRest {
+
+ private Integer id;
+
+ public static final String NAME = "versionhistory";
+ public static final String CATEGORY = RestAddressableModel.VERSIONING;
+ public static final String VERSIONS = "versions";
+
+ @Override
+ public String getCategory() {
+ return CATEGORY;
+ }
+
+ @Override
+ public Class getController() {
+ return RestResourceController.class;
+ }
+
+ @Override
+ public String getType() {
+ return NAME;
+ }
+
+ /**
+ * Generic getter for the id
+ * @return the id value of this VersionHistoryRest
+ */
+ @Override
+ public Integer getId() {
+ return id;
+ }
+
+ /**
+ * Generic setter for the id
+ * @param id The id to be set on this VersionHistoryRest
+ */
+ @Override
+ public void setId(Integer id) {
+ this.id = id;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionRest.java
new file mode 100644
index 0000000000..abdc64295f
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionRest.java
@@ -0,0 +1,140 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.model;
+
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import org.dspace.app.rest.RestResourceController;
+
+/**
+ * The REST object for the {@link org.dspace.versioning.Version} objects
+ */
+@LinksRest(links = {
+ @LinkRest(
+ name = VersionRest.VERSION_HISTORY,
+ method = "getVersionHistory"
+ ),
+ @LinkRest(
+ name = VersionRest.ITEM,
+ method = "getVersionItem"
+ )
+})
+public class VersionRest extends BaseObjectRest {
+
+ public static final String NAME = "version";
+ public static final String CATEGORY = RestAddressableModel.VERSIONING;
+
+ public static final String VERSION_HISTORY = "versionhistory";
+ public static final String ITEM = "item";
+
+ private Integer id;
+ private Integer version;
+ private Date created;
+ private String summary;
+
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ private String submitterName;
+
+ /**
+ * Generic getter for the id
+ * @return the id value of this VersionRest
+ */
+ @Override
+ public Integer getId() {
+ return id;
+ }
+
+ /**
+ * Generic setter for the id
+ * @param id The id to be set on this VersionRest
+ */
+ @Override
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ /**
+ * Generic getter for the version
+ * @return the version value of this VersionRest
+ */
+ public Integer getVersion() {
+ return version;
+ }
+
+ /**
+ * Generic setter for the version
+ * @param version The version to be set on this VersionRest
+ */
+ public void setVersion(Integer version) {
+ this.version = version;
+ }
+
+ /**
+ * Generic getter for the created
+ * @return the created value of this VersionRest
+ */
+ public Date getCreated() {
+ return created;
+ }
+
+ /**
+ * Generic setter for the created
+ * @param created The created to be set on this VersionRest
+ */
+ public void setCreated(Date created) {
+ this.created = created;
+ }
+
+ /**
+ * Generic getter for the summary
+ * @return the summary value of this VersionRest
+ */
+ public String getSummary() {
+ return summary;
+ }
+
+ /**
+ * Generic setter for the summary
+ * @param summary The summary to be set on this VersionRest
+ */
+ public void setSummary(String summary) {
+ this.summary = summary;
+ }
+
+ /**
+ * Generic getter for the submitterName
+ * @return the submitterName value of this VersionRest
+ */
+ public String getSubmitterName() {
+ return submitterName;
+ }
+
+ /**
+ * Generic setter for the submitterName
+ * @param submitterName The submitterName to be set on this VersionRest
+ */
+ public void setSubmitterName(String submitterName) {
+ this.submitterName = submitterName;
+ }
+
+ @Override
+ public String getCategory() {
+ return CATEGORY;
+ }
+
+ @Override
+ public Class getController() {
+ return RestResourceController.class;
+ }
+
+ @Override
+ public String getType() {
+ return NAME;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/AuthorizationFeatureResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/AuthorizationFeatureResource.java
new file mode 100644
index 0000000000..7e27562d60
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/AuthorizationFeatureResource.java
@@ -0,0 +1,25 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.model.hateoas;
+
+import org.dspace.app.rest.model.AuthorizationFeatureRest;
+import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource;
+import org.dspace.app.rest.utils.Utils;
+
+/**
+ * Authorization Feature Rest HAL Resource. The HAL Resource wraps the REST Resource
+ * adding support for the links and embedded resources
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@RelNameDSpaceResource(AuthorizationFeatureRest.NAME)
+public class AuthorizationFeatureResource extends DSpaceResource {
+ public AuthorizationFeatureResource(AuthorizationFeatureRest authzFeature, Utils utils) {
+ super(authzFeature, utils);
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/AuthorizationResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/AuthorizationResource.java
new file mode 100644
index 0000000000..c4f6d8264c
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/AuthorizationResource.java
@@ -0,0 +1,25 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.model.hateoas;
+
+import org.dspace.app.rest.model.AuthorizationRest;
+import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource;
+import org.dspace.app.rest.utils.Utils;
+
+/**
+ * Authorization Rest HAL Resource. The HAL Resource wraps the REST Resource
+ * adding support for the links and embedded resources
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@RelNameDSpaceResource(AuthorizationRest.NAME)
+public class AuthorizationResource extends DSpaceResource {
+ public AuthorizationResource(AuthorizationRest authz, Utils utils) {
+ super(authz, utils);
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/CollectionResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/CollectionResource.java
index 86083daba5..12d16e04ed 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/CollectionResource.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/CollectionResource.java
@@ -12,7 +12,7 @@ import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource;
import org.dspace.app.rest.utils.Utils;
/**
- * Item Rest HAL Resource. The HAL Resource wraps the REST Resource
+ * Collection Rest HAL Resource. The HAL Resource wraps the REST Resource
* adding support for the links and embedded resources
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/VersionHistoryResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/VersionHistoryResource.java
new file mode 100644
index 0000000000..9c7c31f955
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/VersionHistoryResource.java
@@ -0,0 +1,23 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.model.hateoas;
+
+import org.dspace.app.rest.model.VersionHistoryRest;
+import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource;
+import org.dspace.app.rest.utils.Utils;
+
+/**
+ * The HALResource object for the {@link VersionHistoryRest} object
+ */
+@RelNameDSpaceResource(VersionHistoryRest.NAME)
+public class VersionHistoryResource extends DSpaceResource {
+
+ public VersionHistoryResource(VersionHistoryRest data, Utils utils) {
+ super(data, utils);
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/VersionResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/VersionResource.java
new file mode 100644
index 0000000000..1de1617359
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/VersionResource.java
@@ -0,0 +1,24 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.model.hateoas;
+
+import org.dspace.app.rest.model.VersionRest;
+import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource;
+import org.dspace.app.rest.utils.Utils;
+
+/**
+ * The HALResource object for the {@link VersionRest} object
+ */
+@RelNameDSpaceResource(VersionRest.NAME)
+public class VersionResource extends DSpaceResource {
+ public VersionResource(VersionRest data, Utils utils) {
+ super(data, utils);
+ }
+
+
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/UploadBitstreamRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/UploadBitstreamRest.java
index 7024e5cdb1..58e8fc157c 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/UploadBitstreamRest.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/UploadBitstreamRest.java
@@ -8,7 +8,6 @@
package org.dspace.app.rest.model.step;
import java.util.ArrayList;
-
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -93,4 +92,4 @@ public class UploadBitstreamRest extends UploadStatusResponse {
public void setFormat(BitstreamFormatRest format) {
this.format = format;
}
-}
\ No newline at end of file
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AuthorizationEpersonLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AuthorizationEpersonLinkRepository.java
new file mode 100644
index 0000000000..5ffbd95a77
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AuthorizationEpersonLinkRepository.java
@@ -0,0 +1,52 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.repository;
+
+import java.sql.SQLException;
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+
+import org.dspace.app.rest.authorization.AuthorizationRestUtil;
+import org.dspace.app.rest.model.AuthorizationRest;
+import org.dspace.app.rest.model.EPersonRest;
+import org.dspace.app.rest.projection.Projection;
+import org.dspace.core.Context;
+import org.dspace.eperson.EPerson;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+/**
+ * Link repository for "eperson" subresource of an individual authorization.
+ */
+@Component(AuthorizationRest.CATEGORY + "." + AuthorizationRest.NAME + "." + AuthorizationRest.EPERSON)
+public class AuthorizationEpersonLinkRepository extends AbstractDSpaceRestRepository
+ implements LinkRestRepository {
+
+ @Autowired
+ private AuthorizationRestUtil authorizationRestUtil;
+
+ @PreAuthorize("hasPermission(#authzId, 'AUTHORIZATION', 'READ')")
+ public EPersonRest getEperson(@Nullable HttpServletRequest request,
+ String authzId,
+ @Nullable Pageable optionalPageable,
+ Projection projection) {
+ Context context = obtainContext();
+ EPerson eperson;
+ try {
+ eperson = authorizationRestUtil.getEperson(context, authzId);
+ } catch (SQLException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ if (eperson == null) {
+ return null;
+ }
+ return converter.toRest(eperson, projection);
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AuthorizationFeatureLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AuthorizationFeatureLinkRepository.java
new file mode 100644
index 0000000000..cbfa848df3
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AuthorizationFeatureLinkRepository.java
@@ -0,0 +1,49 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.repository;
+
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+
+import org.dspace.app.rest.authorization.AuthorizationFeature;
+import org.dspace.app.rest.authorization.AuthorizationFeatureService;
+import org.dspace.app.rest.authorization.AuthorizationRestUtil;
+import org.dspace.app.rest.model.AuthorizationFeatureRest;
+import org.dspace.app.rest.model.AuthorizationRest;
+import org.dspace.app.rest.projection.Projection;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+/**
+ * Link repository for "feature" subresource of an individual authorization.
+ */
+@Component(AuthorizationRest.CATEGORY + "." + AuthorizationRest.NAME + "." + AuthorizationRest.FEATURE)
+public class AuthorizationFeatureLinkRepository extends AbstractDSpaceRestRepository
+ implements LinkRestRepository {
+
+ @Autowired
+ private AuthorizationRestUtil authorizationRestUtil;
+
+ @Autowired
+ private AuthorizationFeatureService authorizationFeatureService;
+
+ @PreAuthorize("hasPermission(#authzId, 'AUTHORIZATION', 'READ')")
+ public AuthorizationFeatureRest getFeature(@Nullable HttpServletRequest request,
+ String authzId,
+ @Nullable Pageable optionalPageable,
+ Projection projection) {
+ String featureName = authorizationRestUtil.getFeatureName(authzId);
+ AuthorizationFeature feature = authorizationFeatureService.find(featureName);
+ if (feature == null) {
+ return null;
+ }
+ return converter.toRest(feature, projection);
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AuthorizationFeatureRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AuthorizationFeatureRestRepository.java
new file mode 100644
index 0000000000..418bebcdf5
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AuthorizationFeatureRestRepository.java
@@ -0,0 +1,69 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.repository;
+
+import java.util.List;
+
+import org.dspace.app.rest.Parameter;
+import org.dspace.app.rest.SearchRestMethod;
+import org.dspace.app.rest.authorization.AuthorizationFeature;
+import org.dspace.app.rest.authorization.AuthorizationFeatureService;
+import org.dspace.app.rest.converter.ConverterService;
+import org.dspace.app.rest.model.AuthorizationFeatureRest;
+import org.dspace.core.Context;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+
+/**
+ * This is the repository responsible to manage AuthorizationFeature Rest object
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@Component(AuthorizationFeatureRest.CATEGORY + "." + AuthorizationFeatureRest.NAME)
+public class AuthorizationFeatureRestRepository extends DSpaceRestRepository {
+
+ @Autowired
+ private AuthorizationFeatureService authorizationFeatureService;
+
+ @Autowired
+ protected ConverterService converter;
+
+ @Override
+ public Class getDomainClass() {
+ return AuthorizationFeatureRest.class;
+ }
+
+ @PreAuthorize("hasAuthority('ADMIN')")
+ @Override
+ public Page findAll(Context context, Pageable pageable) {
+ return converter.toRestPage(utils.getPage(authorizationFeatureService.findAll(),
+ pageable), utils.obtainProjection());
+ }
+
+ @PreAuthorize("hasAuthority('ADMIN')")
+ @Override
+ public AuthorizationFeatureRest findOne(Context context, String id) {
+ AuthorizationFeature authzFeature = authorizationFeatureService.find(id);
+ if (authzFeature != null) {
+ return converter.toRest(authzFeature, utils.obtainProjection());
+ }
+ return null;
+ }
+
+ @PreAuthorize("hasAuthority('ADMIN')")
+ @SearchRestMethod(name = "resourcetype")
+ public Page findByResourceType(@Parameter(value = "type", required = true) String type,
+ Pageable pageable) {
+ List foundFeatures = authorizationFeatureService.findByResourceType(type);
+ return converter.toRestPage(utils.getPage(foundFeatures, pageable), utils.obtainProjection());
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AuthorizationObjectLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AuthorizationObjectLinkRepository.java
new file mode 100644
index 0000000000..f010b28fa5
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AuthorizationObjectLinkRepository.java
@@ -0,0 +1,48 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.repository;
+
+import java.sql.SQLException;
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+
+import org.dspace.app.rest.authorization.AuthorizationRestUtil;
+import org.dspace.app.rest.model.AuthorizationRest;
+import org.dspace.app.rest.model.BaseObjectRest;
+import org.dspace.app.rest.projection.Projection;
+import org.dspace.core.Context;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+/**
+ * Link repository for "object" subresource of an individual authorization.
+ */
+@Component(AuthorizationRest.CATEGORY + "." + AuthorizationRest.NAME + "." + AuthorizationRest.OBJECT)
+public class AuthorizationObjectLinkRepository extends AbstractDSpaceRestRepository
+ implements LinkRestRepository {
+
+ @Autowired
+ private AuthorizationRestUtil authorizationRestUtil;
+
+ @PreAuthorize("hasPermission(#authzId, 'AUTHORIZATION', 'READ')")
+ public BaseObjectRest getObject(@Nullable HttpServletRequest request,
+ String authzId,
+ @Nullable Pageable optionalPageable,
+ Projection projection) {
+ Context context = obtainContext();
+ BaseObjectRest object;
+ try {
+ object = authorizationRestUtil.getObject(context, authzId);
+ } catch (SQLException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ return object;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AuthorizationRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AuthorizationRestRepository.java
new file mode 100644
index 0000000000..74f5cfdc9b
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AuthorizationRestRepository.java
@@ -0,0 +1,274 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.repository;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.dspace.app.rest.Parameter;
+import org.dspace.app.rest.SearchRestMethod;
+import org.dspace.app.rest.authorization.Authorization;
+import org.dspace.app.rest.authorization.AuthorizationFeature;
+import org.dspace.app.rest.authorization.AuthorizationFeatureService;
+import org.dspace.app.rest.authorization.AuthorizationRestUtil;
+import org.dspace.app.rest.converter.ConverterService;
+import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
+import org.dspace.app.rest.model.AuthorizationRest;
+import org.dspace.app.rest.model.BaseObjectRest;
+import org.dspace.authorize.AuthorizeException;
+import org.dspace.authorize.service.AuthorizeService;
+import org.dspace.core.Context;
+import org.dspace.eperson.EPerson;
+import org.dspace.eperson.service.EPersonService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+
+/**
+ * This is the repository responsible to manage Authorization Rest object
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+
+@Component(AuthorizationRest.CATEGORY + "." + AuthorizationRest.NAME)
+public class AuthorizationRestRepository extends DSpaceRestRepository {
+
+ private static final Logger log = LoggerFactory.getLogger(AuthorizationRestRepository.class);
+
+ @Autowired
+ private AuthorizationFeatureService authorizationFeatureService;
+
+ @Autowired
+ private AuthorizationRestUtil authorizationRestUtil;
+
+ @Autowired
+ private AuthorizeService authorizeService;
+
+ @Autowired
+ private EPersonService epersonService;
+
+ @Autowired
+ protected ConverterService converter;
+
+ @Override
+ @PreAuthorize("hasPermission(#id, 'authorization', 'READ')")
+ public AuthorizationRest findOne(Context context, String id) {
+
+ AuthorizationRest authorizationRest = null;
+
+ String featureName;
+ try {
+ featureName = authorizationRestUtil.getFeatureName(id);
+ } catch (IllegalArgumentException e) {
+ log.warn(e.getMessage(), e);
+ return null;
+ }
+ try {
+ BaseObjectRest object = null;
+ try {
+ object = authorizationRestUtil.getObject(context, id);
+ } catch (IllegalArgumentException e) {
+ log.warn("Object informations not found in the specified id " + id, e);
+ return null;
+ }
+
+ AuthorizationFeature authorizationFeature = null;
+ if (featureName != null) {
+ authorizationFeature = authorizationFeatureService.find(featureName);
+ }
+
+ if (authorizationFeature == null) {
+ return null;
+ }
+ // get the user specified identified by the id, can be null for anonymous
+ EPerson user;
+ try {
+ user = authorizationRestUtil.getEperson(context, id);
+ } catch (IllegalArgumentException e) {
+ log.warn("Invalid eperson informations in the specified id " + id, e);
+ return null;
+ }
+ EPerson currUser = context.getCurrentUser();
+ if (currUser != user) {
+ // Temporarily change the Context's current user in order to retrieve
+ // authorizations based on that user
+ context.switchContextUser(user);
+ }
+
+ if (authorizationFeatureService.isAuthorized(context, authorizationFeature, object)) {
+ Authorization authz = new Authorization();
+ authz.setEperson(user);
+ authz.setFeature(authorizationFeature);
+ authz.setObject(object);
+ authorizationRest = converter.toRest(authz, utils.obtainProjection());
+ }
+ if (currUser != user) {
+ // restore the real current user
+ context.restoreContextUser();
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+
+ return authorizationRest;
+ }
+
+
+ /**
+ * It returns the list of matching available authorizations granted to the specified eperson or to the anonymous
+ * user. Only administrators and the user identified by the epersonUuid parameter can access this method
+ *
+ * @param uri
+ * the uri of the object to check the authorization against
+ * @param epersonUuid
+ * the eperson uuid to use in the authorization evaluation
+ * @param pageable
+ * the pagination options
+ * @return the list of matching authorization available for the requested user and object, filtered by feature if
+ * provided
+ * @throws AuthorizeException
+ * @throws SQLException
+ */
+ @PreAuthorize("#epersonUuid==null || hasPermission(#epersonUuid, 'EPERSON', 'READ')")
+ @SearchRestMethod(name = "object")
+ public Page findByObject(@Parameter(value = "uri", required = true) String uri,
+ @Parameter(value = "eperson") UUID epersonUuid,
+ Pageable pageable) throws AuthorizeException, SQLException {
+ Context context = obtainContext();
+ BaseObjectRest obj = utils.getBaseObjectRestFromUri(context, uri);
+ if (obj == null) {
+ return null;
+ }
+
+ EPerson currUser = context.getCurrentUser();
+ // get the user specified in the requested parameters, can be null for anonymous
+ EPerson user = getUserFromRequestParameter(context, epersonUuid);
+ if (currUser != user) {
+ // Temporarily change the Context's current user in order to retrieve
+ // authorizations based on that user
+ context.switchContextUser(user);
+ }
+
+ List features = authorizationFeatureService.findByResourceType(obj.getUniqueType());
+ List authorizations = new ArrayList();
+ for (AuthorizationFeature f : features) {
+ if (authorizationFeatureService.isAuthorized(context, f, obj)) {
+ authorizations.add(new Authorization(user, f, obj));
+ }
+ }
+
+ if (currUser != user) {
+ // restore the real current user
+ context.restoreContextUser();
+ }
+ return converter.toRestPage(utils.getPage(authorizations, pageable), utils.obtainProjection());
+ }
+
+ /**
+ * It returns the authorization related to the requested feature if granted to the specified eperson or to the
+ * anonymous user. Only administrators and the user identified by the epersonUuid parameter can access this method
+ *
+ * @param uri
+ * the uri of the object to check the authorization against
+ * @param epersonUuid
+ * the eperson uuid to use in the authorization evaluation
+ * @param featureName
+ * limit the authorization check to only the feature identified via its name
+ * @param pageable
+ * the pagination options
+ * @return the list of matching authorization available for the requested user and object, filtered by feature if
+ * provided
+ * @throws AuthorizeException
+ * @throws SQLException
+ */
+ @PreAuthorize("#epersonUuid==null || hasPermission(#epersonUuid, 'EPERSON', 'READ')")
+ @SearchRestMethod(name = "objectAndFeature")
+ public AuthorizationRest findByObjectAndFeature(@Parameter(value = "uri", required = true) String uri,
+ @Parameter(value = "eperson") UUID epersonUuid,
+ @Parameter(value = "feature", required = true) String featureName,
+ Pageable pageable) throws AuthorizeException, SQLException {
+ Context context = obtainContext();
+ BaseObjectRest obj = utils.getBaseObjectRestFromUri(context, uri);
+ if (obj == null) {
+ return null;
+ }
+
+ EPerson currUser = context.getCurrentUser();
+ // get the user specified in the requested parameters, can be null for anonymous
+ EPerson user = getUserFromRequestParameter(context, epersonUuid);
+ if (currUser != user) {
+ // Temporarily change the Context's current user in order to retrieve
+ // authorizations based on that user
+ context.switchContextUser(user);
+ }
+ AuthorizationFeature feature = authorizationFeatureService.find(featureName);
+ AuthorizationRest authorizationRest = null;
+ if (authorizationFeatureService.isAuthorized(context, feature, obj)) {
+ Authorization authz = new Authorization();
+ authz.setEperson(user);
+ authz.setFeature(feature);
+ authz.setObject(obj);
+ authorizationRest = converter.toRest(authz, utils.obtainProjection());
+ }
+ if (currUser != user) {
+ // restore the real current user
+ context.restoreContextUser();
+ }
+ return authorizationRest;
+ }
+
+ /**
+ * Return the user specified in the request parameter if valid
+ *
+ * @param context
+ * @param epersonUuid
+ * @return
+ * @throws AuthorizeException if the user specified in the request parameter is
+ * not valid
+ * @throws SQLException if a database error occurs
+ */
+ private EPerson getUserFromRequestParameter(Context context, UUID epersonUuid)
+ throws AuthorizeException, SQLException {
+ EPerson currUser = context.getCurrentUser();
+ EPerson user = currUser;
+ if (epersonUuid != null) {
+ if (currUser == null) {
+ throw new AuthorizeException("attempt to anonymously access the authorization of the eperson "
+ + epersonUuid);
+ } else {
+ // an user is specified in the request parameters
+ if (!authorizeService.isAdmin(context) && !epersonUuid.equals(currUser.getID())) {
+ throw new AuthorizeException("attempt to access the authorization of the eperson " + epersonUuid
+ + " only system administrators can see the authorization of other users");
+ }
+ user = epersonService.find(context, epersonUuid);
+ }
+ } else {
+ // the request asks to check the permission for the anonymous user
+ user = null;
+ }
+ return user;
+ }
+
+ @Override
+ public Class getDomainClass() {
+ return AuthorizationRest.class;
+ }
+
+ @Override
+ public Page findAll(Context context, Pageable pageable) {
+ throw new RepositoryMethodNotImplementedException(AuthorizationRest.NAME, "findAll");
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CollectionRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CollectionRestRepository.java
index 4ff8e6b174..73ba5111f5 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CollectionRestRepository.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CollectionRestRepository.java
@@ -9,6 +9,7 @@ package org.dspace.app.rest.repository;
import java.io.IOException;
import java.sql.SQLException;
+import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import javax.servlet.ServletInputStream;
@@ -29,6 +30,7 @@ import org.dspace.app.rest.model.patch.Patch;
import org.dspace.app.rest.model.wrapper.TemplateItem;
import org.dspace.app.rest.utils.CollectionRestEqualityUtils;
import org.dspace.authorize.AuthorizeException;
+import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Bitstream;
import org.dspace.content.Collection;
import org.dspace.content.Community;
@@ -39,6 +41,12 @@ import org.dspace.content.service.CommunityService;
import org.dspace.content.service.ItemService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
+import org.dspace.discovery.DiscoverQuery;
+import org.dspace.discovery.DiscoverResult;
+import org.dspace.discovery.IndexableObject;
+import org.dspace.discovery.SearchService;
+import org.dspace.discovery.SearchServiceException;
+import org.dspace.discovery.indexobject.IndexableCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -71,6 +79,12 @@ public class CollectionRestRepository extends DSpaceObjectRestRepository findAll(Context context, Pageable pageable) {
try {
- long total = cs.countTotal(context);
- List collections = cs.findAll(context, pageable.getPageSize(),
- Math.toIntExact(pageable.getOffset()));
- return converter.toRestPage(collections, pageable, total, utils.obtainProjection());
- } catch (SQLException e) {
+ if (authorizeService.isAdmin(context)) {
+ long total = cs.countTotal(context);
+ List collections = cs.findAll(context, pageable.getPageSize(),
+ Math.toIntExact(pageable.getOffset()));
+ return converter.toRestPage(collections, pageable, total, utils.obtainProjection());
+ } else {
+ List collections = new LinkedList();
+ // search for all the collections and let the SOLR security plugins to limit
+ // what is returned to what the user can see
+ DiscoverQuery discoverQuery = new DiscoverQuery();
+ discoverQuery.setDSpaceObjectFilter(IndexableCollection.TYPE);
+ discoverQuery.setStart(Math.toIntExact(pageable.getOffset()));
+ discoverQuery.setMaxResults(pageable.getPageSize());
+ DiscoverResult resp = searchService.search(context, discoverQuery);
+ long tot = resp.getTotalSearchResults();
+ for (IndexableObject solrCollections : resp.getIndexableObjects()) {
+ Collection c = ((IndexableCollection) solrCollections).getIndexedObject();
+ collections.add(c);
+ }
+ return converter.toRestPage(collections, pageable, tot, utils.obtainProjection());
+ }
+ } catch (SQLException | SearchServiceException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CommunityCollectionLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CommunityCollectionLinkRepository.java
index 0b7fa4bfe1..eae33f9025 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CommunityCollectionLinkRepository.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CommunityCollectionLinkRepository.java
@@ -8,6 +8,7 @@
package org.dspace.app.rest.repository;
import java.sql.SQLException;
+import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
@@ -20,6 +21,13 @@ import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.service.CommunityService;
import org.dspace.core.Context;
+import org.dspace.discovery.DiscoverQuery;
+import org.dspace.discovery.DiscoverResult;
+import org.dspace.discovery.IndexableObject;
+import org.dspace.discovery.SearchService;
+import org.dspace.discovery.SearchServiceException;
+import org.dspace.discovery.indexobject.IndexableCollection;
+import org.dspace.discovery.indexobject.factory.IndexObjectFactoryFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -37,6 +45,9 @@ public class CommunityCollectionLinkRepository extends AbstractDSpaceRestReposit
@Autowired
CommunityService communityService;
+ @Autowired
+ SearchService searchService;
+
@PreAuthorize("hasPermission(#communityId, 'COMMUNITY', 'READ')")
public Page getCollections(@Nullable HttpServletRequest request,
UUID communityId,
@@ -48,10 +59,27 @@ public class CommunityCollectionLinkRepository extends AbstractDSpaceRestReposit
if (community == null) {
throw new ResourceNotFoundException("No such community: " + communityId);
}
- List collections = community.getCollections();
- return converter.toRestPage(utils.getPage(collections, optionalPageable), projection);
- } catch (SQLException e) {
- throw new RuntimeException(e);
+ Pageable pageable = utils.getPageable(optionalPageable);
+ List collections = new LinkedList();
+ IndexObjectFactoryFactory indexObjectFactory = IndexObjectFactoryFactory.getInstance();
+ IndexableObject scopeObject = indexObjectFactory.getIndexableObjects(context, community).get(0);
+ // search for all the collections direct children of our community
+ // and let the SOLR security plugins to limit what is returned to what the user can see
+ DiscoverQuery discoverQuery = new DiscoverQuery();
+ discoverQuery.setQuery("*:*");
+ discoverQuery.setDSpaceObjectFilter(IndexableCollection.TYPE);
+ discoverQuery.addFilterQueries("location.parent:" + communityId);
+ discoverQuery.setStart(Math.toIntExact(pageable.getOffset()));
+ discoverQuery.setMaxResults(pageable.getPageSize());
+ DiscoverResult resp = searchService.search(context, scopeObject, discoverQuery);
+ long tot = resp.getTotalSearchResults();
+ for (IndexableObject solrCol : resp.getIndexableObjects()) {
+ Collection c = ((IndexableCollection) solrCol).getIndexedObject();
+ collections.add(c);
+ }
+ return converter.toRestPage(collections, pageable, tot, utils.obtainProjection());
+ } catch (SQLException | SearchServiceException e) {
+ throw new RuntimeException(e.getMessage(), e);
}
}
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CommunityRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CommunityRestRepository.java
index f17f7d950b..5ed465d42b 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CommunityRestRepository.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CommunityRestRepository.java
@@ -9,6 +9,7 @@ package org.dspace.app.rest.repository;
import java.io.IOException;
import java.sql.SQLException;
+import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import javax.servlet.ServletInputStream;
@@ -17,7 +18,6 @@ import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.logging.log4j.Logger;
-import org.dspace.app.rest.Parameter;
import org.dspace.app.rest.SearchRestMethod;
import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
@@ -27,11 +27,18 @@ import org.dspace.app.rest.model.CommunityRest;
import org.dspace.app.rest.model.patch.Patch;
import org.dspace.app.rest.utils.CommunityRestEqualityUtils;
import org.dspace.authorize.AuthorizeException;
+import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Bitstream;
import org.dspace.content.Community;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.CommunityService;
import org.dspace.core.Context;
+import org.dspace.discovery.DiscoverQuery;
+import org.dspace.discovery.DiscoverResult;
+import org.dspace.discovery.IndexableObject;
+import org.dspace.discovery.SearchService;
+import org.dspace.discovery.SearchServiceException;
+import org.dspace.discovery.indexobject.IndexableCommunity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -59,6 +66,11 @@ public class CommunityRestRepository extends DSpaceObjectRestRepository findAll(Context context, Pageable pageable) {
try {
- long total = cs.countTotal(context);
- List communities = cs.findAll(context, pageable.getPageSize(),
+ if (authorizeService.isAdmin(context)) {
+ long total = cs.countTotal(context);
+ List communities = cs.findAll(context, pageable.getPageSize(),
Math.toIntExact(pageable.getOffset()));
- return converter.toRestPage(communities, pageable, total, utils.obtainProjection());
- } catch (SQLException e) {
+ return converter.toRestPage(communities, pageable, total, utils.obtainProjection());
+ } else {
+ List communities = new LinkedList();
+ // search for all the communities and let the SOLR security plugins to limit
+ // what is returned to what the user can see
+ DiscoverQuery discoverQuery = new DiscoverQuery();
+ discoverQuery.setDSpaceObjectFilter(IndexableCommunity.TYPE);
+ discoverQuery.setStart(Math.toIntExact(pageable.getOffset()));
+ discoverQuery.setMaxResults(pageable.getPageSize());
+ DiscoverResult resp = searchService.search(context, discoverQuery);
+ long tot = resp.getTotalSearchResults();
+ for (IndexableObject solrCommunities : resp.getIndexableObjects()) {
+ Community c = ((IndexableCommunity) solrCommunities).getIndexedObject();
+ communities.add(c);
+ }
+ return converter.toRestPage(communities, pageable, tot, utils.obtainProjection());
+ }
+ } catch (SQLException | SearchServiceException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
@@ -168,25 +197,6 @@ public class CommunityRestRepository extends DSpaceObjectRestRepository findSubCommunities(@Parameter(value = "parent", required = true) UUID parentCommunity,
- Pageable pageable) {
- Context context = obtainContext();
- try {
- Community community = cs.find(context, parentCommunity);
- if (community == null) {
- throw new ResourceNotFoundException(
- CommunityRest.CATEGORY + "." + CommunityRest.NAME + " with id: " + parentCommunity + " not found");
- }
- List subCommunities = community.getSubcommunities();
- return converter.toRestPage(utils.getPage(subCommunities, pageable), utils.obtainProjection());
- } catch (SQLException e) {
- throw new RuntimeException(e.getMessage(), e);
- }
- }
-
@Override
@PreAuthorize("hasPermission(#id, 'COMMUNITY', 'WRITE')")
protected void patch(Context context, HttpServletRequest request, String apiCategory, String model, UUID id,
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CommunitySubcommunityLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CommunitySubcommunityLinkRepository.java
index 8767aca43e..7b74b40557 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CommunitySubcommunityLinkRepository.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CommunitySubcommunityLinkRepository.java
@@ -8,6 +8,7 @@
package org.dspace.app.rest.repository;
import java.sql.SQLException;
+import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
@@ -18,6 +19,13 @@ import org.dspace.app.rest.projection.Projection;
import org.dspace.content.Community;
import org.dspace.content.service.CommunityService;
import org.dspace.core.Context;
+import org.dspace.discovery.DiscoverQuery;
+import org.dspace.discovery.DiscoverResult;
+import org.dspace.discovery.IndexableObject;
+import org.dspace.discovery.SearchService;
+import org.dspace.discovery.SearchServiceException;
+import org.dspace.discovery.indexobject.IndexableCommunity;
+import org.dspace.discovery.indexobject.factory.IndexObjectFactoryFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -35,6 +43,9 @@ public class CommunitySubcommunityLinkRepository extends AbstractDSpaceRestRepos
@Autowired
CommunityService communityService;
+ @Autowired
+ SearchService searchService;
+
@PreAuthorize("hasPermission(#communityId, 'COMMUNITY', 'READ')")
public Page getSubcommunities(@Nullable HttpServletRequest request,
UUID communityId,
@@ -46,10 +57,25 @@ public class CommunitySubcommunityLinkRepository extends AbstractDSpaceRestRepos
if (community == null) {
throw new ResourceNotFoundException("No such community: " + communityId);
}
- List subcommunities = community.getSubcommunities();
- return converter.toRestPage(utils.getPage(subcommunities, optionalPageable), projection);
- } catch (SQLException e) {
- throw new RuntimeException(e);
+ Pageable pageable = utils.getPageable(optionalPageable);
+ List publicSubcommunities = new LinkedList();
+ IndexObjectFactoryFactory indexObjectFactory = IndexObjectFactoryFactory.getInstance();
+ IndexableObject scopeObject = indexObjectFactory.getIndexableObjects(context, community).get(0);
+ DiscoverQuery discoverQuery = new DiscoverQuery();
+ discoverQuery.setQuery("*:*");
+ discoverQuery.setDSpaceObjectFilter(IndexableCommunity.TYPE);
+ discoverQuery.addFilterQueries("location.parent:" + communityId);
+ discoverQuery.setStart(Math.toIntExact(pageable.getOffset()));
+ discoverQuery.setMaxResults(pageable.getPageSize());
+ DiscoverResult resp = searchService.search(context, scopeObject, discoverQuery);
+ long tot = resp.getTotalSearchResults();
+ for (IndexableObject solrCommunities : resp.getIndexableObjects()) {
+ Community c = ((IndexableCommunity) solrCommunities).getIndexedObject();
+ publicSubcommunities.add(c);
+ }
+ return converter.toRestPage(publicSubcommunities, pageable, tot, utils.obtainProjection());
+ } catch (SQLException | SearchServiceException e) {
+ throw new RuntimeException(e.getMessage(), e);
}
}
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/DSpaceObjectRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/DSpaceObjectRestRepository.java
index ef254df1b1..18c18d7af1 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/DSpaceObjectRestRepository.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/DSpaceObjectRestRepository.java
@@ -29,7 +29,7 @@ import org.springframework.data.rest.webmvc.ResourceNotFoundException;
* @param the corresponding DSpaceObjectRest.
*/
public abstract class DSpaceObjectRestRepository
- extends DSpaceRestRepository {
+ extends DSpaceRestRepository implements ReloadableEntityObjectRepository {
final DSpaceObjectService dsoService;
@@ -64,4 +64,14 @@ public abstract class DSpaceObjectRestRepository getPKClass() {
+ return UUID.class;
+ }
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/DSpaceRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/DSpaceRestRepository.java
index 7183b4ece7..97c13a6656 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/DSpaceRestRepository.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/DSpaceRestRepository.java
@@ -14,7 +14,6 @@ import java.sql.SQLException;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
-
import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.JsonNode;
@@ -382,7 +381,8 @@ public abstract class DSpaceRestRepository the ReloadableEntity type
+ * @param the primary key type
+ */
+public interface ReloadableEntityObjectRepository,
+ PK extends Serializable> {
+
+ /**
+ *
+ * @param context the DSpace context
+ * @param id the primary key shared between the rest and dspace api object
+ * @return the dspace api model object related to the specified id
+ * @throws SQLException if a database error occurs
+ */
+ T findDomainObjectByPk(Context context, PK id) throws SQLException;
+
+ /**
+ *
+ * @return the class of the primary key
+ */
+ Class getPKClass();
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java
index f7640dcc2b..009e0c4594 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java
@@ -11,11 +11,9 @@ import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
-
import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
-
import org.dspace.app.rest.Parameter;
import org.dspace.app.rest.SearchRestMethod;
import org.dspace.app.rest.exception.MissingParameterException;
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VersionHistoryLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VersionHistoryLinkRepository.java
new file mode 100644
index 0000000000..6456a0c2b5
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VersionHistoryLinkRepository.java
@@ -0,0 +1,64 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.repository;
+
+import java.sql.SQLException;
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+
+import org.dspace.app.rest.model.VersionHistoryRest;
+import org.dspace.app.rest.model.VersionRest;
+import org.dspace.app.rest.projection.Projection;
+import org.dspace.core.Context;
+import org.dspace.versioning.Version;
+import org.dspace.versioning.VersionHistory;
+import org.dspace.versioning.service.VersioningService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.rest.webmvc.ResourceNotFoundException;
+import org.springframework.stereotype.Component;
+
+/**
+ * This is the Repository that takes care of the retrieval of the {@link VersionHistory} object
+ * for a given {@link Version}
+ */
+@Component(VersionRest.CATEGORY + "." + VersionRest.NAME + "." + VersionRest.VERSION_HISTORY)
+public class VersionHistoryLinkRepository extends AbstractDSpaceRestRepository
+ implements LinkRestRepository {
+
+ @Autowired
+ private VersioningService versioningService;
+
+ /**
+ * This method will retrieve the VersionHistoryRest object from the Version that is found by the associated
+ * versionId parameter
+ * @param request The current request
+ * @param versionId The ID for the Version object
+ * @param optionalPageable The pageable if present
+ * @param projection The current Projection
+ * @return The VersionHistoryRest object that is constructed from the VersionHistory object that
+ * is linked to the Version found by the versionId parameter
+ * @throws SQLException If something goes wrong
+ */
+ public VersionHistoryRest getVersionHistory(@Nullable HttpServletRequest request,
+ Integer versionId,
+ @Nullable Pageable optionalPageable,
+ Projection projection) throws SQLException {
+
+ Context context = obtainContext();
+ Version version = versioningService.getVersion(context, versionId);
+ if (version == null) {
+ throw new ResourceNotFoundException("The version with ID: " + versionId + " couldn't be found");
+ }
+ VersionHistory versionHistory = version.getVersionHistory();
+ if (versionHistory == null) {
+ return null;
+ }
+ return converter.toRest(versionHistory, projection);
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VersionHistoryRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VersionHistoryRestRepository.java
new file mode 100644
index 0000000000..6d1175e80b
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VersionHistoryRestRepository.java
@@ -0,0 +1,64 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.repository;
+
+import java.sql.SQLException;
+
+import org.apache.logging.log4j.Logger;
+import org.dspace.app.rest.converter.ConverterService;
+import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
+import org.dspace.app.rest.model.VersionHistoryRest;
+import org.dspace.core.Context;
+import org.dspace.versioning.VersionHistory;
+import org.dspace.versioning.service.VersionHistoryService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.rest.webmvc.ResourceNotFoundException;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+/**
+ * Repository for the operations on the {@link VersionHistoryRest} endpoints
+ */
+@Component(VersionHistoryRest.CATEGORY + "." + VersionHistoryRest.NAME)
+public class VersionHistoryRestRepository extends DSpaceRestRepository {
+
+ private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(VersionHistoryRestRepository.class);
+
+ @Autowired
+ private VersionHistoryService versionHistoryService;
+
+ @Autowired
+ private ConverterService converterService;
+
+ @Override
+ @PreAuthorize("hasPermission(#id, 'VERSIONHISTORY', 'READ')")
+ public VersionHistoryRest findOne(Context context, Integer id) {
+ try {
+ VersionHistory versionHistory = versionHistoryService.find(context, id);
+ if (versionHistory == null) {
+ throw new ResourceNotFoundException("Couldn't find version for id: " + id);
+ }
+ return converterService.toRest(versionHistory, utils.obtainProjection());
+ } catch (SQLException e) {
+ log.error("Something with wrong getting version with id:" + id, e);
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public Page findAll(Context context, Pageable pageable) {
+ throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", "");
+ }
+
+ @Override
+ public Class getDomainClass() {
+ return VersionHistoryRest.class;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VersionItemLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VersionItemLinkRepository.java
new file mode 100644
index 0000000000..4fd692506b
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VersionItemLinkRepository.java
@@ -0,0 +1,64 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.repository;
+
+import java.sql.SQLException;
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+
+import org.dspace.app.rest.model.ItemRest;
+import org.dspace.app.rest.model.VersionRest;
+import org.dspace.app.rest.projection.Projection;
+import org.dspace.content.Item;
+import org.dspace.core.Context;
+import org.dspace.versioning.Version;
+import org.dspace.versioning.service.VersioningService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.rest.webmvc.ResourceNotFoundException;
+import org.springframework.stereotype.Component;
+
+/**
+ * This Repository takes care of the retrieval of the {@link org.dspace.content.Item} objects
+ * for a given {@link Version}
+ */
+@Component(VersionRest.CATEGORY + "." + VersionRest.NAME + "." + VersionRest.ITEM)
+public class VersionItemLinkRepository extends AbstractDSpaceRestRepository
+ implements LinkRestRepository {
+
+ @Autowired
+ private VersioningService versioningService;
+
+ /**
+ * This method will return the ItemRest object constructed from the Item object which is found in the Version
+ * that will be found through the versionId parameter
+ * @param request The current request
+ * @param versionId The ID for the Version to be used
+ * @param optionalPageable The pageable if present
+ * @param projection The current Projection
+ * @return The ItemRest object that is relevant for the Version
+ * @throws SQLException If something goes wrong
+ */
+ public ItemRest getVersionItem(@Nullable HttpServletRequest request,
+ Integer versionId,
+ @Nullable Pageable optionalPageable,
+ Projection projection) throws SQLException {
+
+
+ Context context = obtainContext();
+ Version version = versioningService.getVersion(context, versionId);
+ if (version == null) {
+ throw new ResourceNotFoundException("The version with ID: " + versionId + " couldn't be found");
+ }
+ Item item = version.getItem();
+ if (item == null) {
+ return null;
+ }
+ return converter.toRest(item, projection);
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VersionRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VersionRestRepository.java
new file mode 100644
index 0000000000..a263396cd6
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VersionRestRepository.java
@@ -0,0 +1,64 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.repository;
+
+import java.sql.SQLException;
+
+import org.apache.logging.log4j.Logger;
+import org.dspace.app.rest.converter.ConverterService;
+import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
+import org.dspace.app.rest.model.VersionRest;
+import org.dspace.core.Context;
+import org.dspace.versioning.Version;
+import org.dspace.versioning.service.VersioningService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.rest.webmvc.ResourceNotFoundException;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+/**
+ * This is the Repository that takes care of the operations on the {@link VersionRest} objects
+ */
+@Component(VersionRest.CATEGORY + "." + VersionRest.NAME)
+public class VersionRestRepository extends DSpaceRestRepository {
+
+ private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(VersionRestRepository.class);
+
+ @Autowired
+ private VersioningService versioningService;
+
+ @Autowired
+ private ConverterService converterService;
+
+ @Override
+ @PreAuthorize("hasPermission(#id, 'VERSION', 'READ')")
+ public VersionRest findOne(Context context, Integer id) {
+ try {
+ Version version = versioningService.getVersion(context, id);
+ if (version == null) {
+ throw new ResourceNotFoundException("Couldn't find version for id: " + id);
+ }
+ return converterService.toRest(version, utils.obtainProjection());
+ } catch (SQLException e) {
+ log.error("Something with wrong getting version with id:" + id, e);
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public Page findAll(Context context, Pageable pageable) {
+ throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", "");
+ }
+
+ @Override
+ public Class getDomainClass() {
+ return VersionRest.class;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VersionsLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VersionsLinkRepository.java
new file mode 100644
index 0000000000..ca89a29853
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/VersionsLinkRepository.java
@@ -0,0 +1,69 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.repository;
+
+import java.sql.SQLException;
+import java.util.List;
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+
+import org.dspace.app.rest.model.VersionHistoryRest;
+import org.dspace.app.rest.model.VersionRest;
+import org.dspace.app.rest.projection.Projection;
+import org.dspace.core.Context;
+import org.dspace.versioning.Version;
+import org.dspace.versioning.VersionHistory;
+import org.dspace.versioning.service.VersionHistoryService;
+import org.dspace.versioning.service.VersioningService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.rest.webmvc.ResourceNotFoundException;
+import org.springframework.stereotype.Component;
+
+/**
+ * This is the Repository that takes care of the retrieval of the {@link Version} objects for a given
+ * {@link VersionHistory}
+ */
+@Component(VersionHistoryRest.CATEGORY + "." + VersionHistoryRest.NAME + "." + VersionHistoryRest.VERSIONS)
+public class VersionsLinkRepository extends AbstractDSpaceRestRepository
+ implements LinkRestRepository {
+
+ @Autowired
+ private VersionHistoryService versionHistoryService;
+
+ @Autowired
+ private VersioningService versioningService;
+
+ /**
+ * This method will return a page of VersionRest objects found through the VersionHistory object that is resolved
+ * from the versionHistoryId parameter
+ * @param request The current request
+ * @param versionHistoryId The ID for the VersionHistory to be used
+ * @param optionalPageable The pageable if present
+ * @param projection The current Projection
+ * @return The page containing relevant VersionRest objects
+ * @throws SQLException If something goes wrong
+ */
+ public Page getVersions(@Nullable HttpServletRequest request,
+ Integer versionHistoryId,
+ @Nullable Pageable optionalPageable,
+ Projection projection) throws SQLException {
+
+ Context context = obtainContext();
+ VersionHistory versionHistory = versionHistoryService.find(context, versionHistoryId);
+ if (versionHistory == null) {
+ throw new ResourceNotFoundException("The versionHistory with ID: " + versionHistoryId +
+ " couldn't be found");
+ }
+ List versions = versioningService.getVersionsByHistory(context, versionHistory);
+ Pageable pageable = optionalPageable != null ? optionalPageable : new PageRequest(0, 20);
+ return converter.toRestPage(utils.getPage(versions, pageable), projection);
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java
index 7d1605e109..d2fdb09280 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java
@@ -34,9 +34,12 @@ import org.dspace.app.util.SubmissionConfigReader;
import org.dspace.app.util.SubmissionConfigReaderException;
import org.dspace.app.util.SubmissionStepConfig;
import org.dspace.authorize.AuthorizeException;
+import org.dspace.authorize.service.AuthorizeService;
+import org.dspace.content.Item;
import org.dspace.content.service.BitstreamFormatService;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.ItemService;
+import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.EPersonServiceImpl;
@@ -56,6 +59,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
+import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
@@ -97,6 +101,9 @@ public class WorkflowItemRestRepository extends DSpaceRestRepository wfs;
+ @Autowired
+ AuthorizeService authorizeService;
+
@Autowired
ClaimedTaskService claimedTaskService;
@@ -188,6 +195,7 @@ public class WorkflowItemRestRepository extends DSpaceRestRepository {
+public class WorkspaceItemRestRepository extends DSpaceRestRepository
+ implements ReloadableEntityObjectRepository {
public static final String OPERATION_PATH_SECTIONS = "sections";
@@ -110,6 +115,9 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository findAll(Context context, Pageable pageable) {
try {
@@ -147,7 +155,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository findBySubmitter(@Parameter(value = "uuid", required = true) UUID submitterID,
Pageable pageable) {
@@ -213,7 +221,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository getPKClass() {
+ return Integer.class;
+ }
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyEndDateReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyEndDateReplaceOperation.java
index 628301e213..a71224ea29 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyEndDateReplaceOperation.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyEndDateReplaceOperation.java
@@ -15,7 +15,6 @@ import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.repository.patch.operation.PatchOperation;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.core.Context;
-
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/CustomLogoutHandler.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/CustomLogoutHandler.java
index 4aadd016a1..204eda62dc 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/CustomLogoutHandler.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/CustomLogoutHandler.java
@@ -45,7 +45,7 @@ public class CustomLogoutHandler implements LogoutHandler {
Authentication authentication) {
try {
Context context = ContextUtil.obtainContext(httpServletRequest);
- restAuthenticationService.invalidateAuthenticationData(httpServletRequest, context);
+ restAuthenticationService.invalidateAuthenticationData(httpServletRequest, httpServletResponse, context);
context.commit();
} catch (Exception e) {
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ReadAuthorizationPermissionEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ReadAuthorizationPermissionEvaluatorPlugin.java
new file mode 100644
index 0000000000..7c24be3e1c
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ReadAuthorizationPermissionEvaluatorPlugin.java
@@ -0,0 +1,84 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.security;
+
+import java.io.Serializable;
+import java.sql.SQLException;
+
+import org.apache.commons.lang3.StringUtils;
+import org.dspace.app.rest.authorization.AuthorizationRestUtil;
+import org.dspace.app.rest.model.AuthorizationRest;
+import org.dspace.app.rest.utils.ContextUtil;
+import org.dspace.authorize.service.AuthorizeService;
+import org.dspace.core.Context;
+import org.dspace.eperson.EPerson;
+import org.dspace.services.RequestService;
+import org.dspace.services.model.Request;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+
+/**
+ * {@link RestPermissionEvaluatorPlugin} class that evaluate READ permissions for an Authorization
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ */
+@Component
+public class ReadAuthorizationPermissionEvaluatorPlugin extends RestObjectPermissionEvaluatorPlugin {
+
+ private static final Logger log = LoggerFactory.getLogger(ReadAuthorizationPermissionEvaluatorPlugin.class);
+
+ @Autowired
+ AuthorizeService authorizeService;
+
+ @Autowired
+ private RequestService requestService;
+
+ @Autowired
+ private AuthorizationRestUtil authorizationRestUtil;
+
+ @Override
+ public boolean hasDSpacePermission(Authentication authentication, Serializable targetId, String targetType,
+ DSpaceRestPermission permission) {
+
+ DSpaceRestPermission restPermission = DSpaceRestPermission.convert(permission);
+
+ if (!DSpaceRestPermission.READ.equals(restPermission)
+ || !StringUtils.equalsIgnoreCase(targetType, AuthorizationRest.NAME)) {
+ return false;
+ }
+
+ Request request = requestService.getCurrentRequest();
+ Context context = ContextUtil.obtainContext(request.getServletRequest());
+ try {
+ // admin can always access
+ if (authorizeService.isAdmin(context)) {
+ return true;
+ }
+ EPerson ePerson = authorizationRestUtil.getEperson(context, targetId.toString());
+ EPerson currUser = context.getCurrentUser();
+
+ if (ePerson == null) {
+ // everyone can check authorization for the anonymous user
+ return true;
+ } else {
+ // anonymous user
+ if (currUser != null && currUser.getID().equals(ePerson.getID())) {
+ return true;
+ }
+ }
+ } catch (SQLException e) {
+ log.error(e.getMessage(), e);
+ }
+
+ return false;
+ }
+
+}
\ No newline at end of file
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java
index ca92ea7503..1740606eb1 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java
@@ -8,7 +8,6 @@
package org.dspace.app.rest.security;
import java.io.Serializable;
-
import java.sql.SQLException;
import org.apache.commons.lang3.StringUtils;
@@ -29,9 +28,9 @@ import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
/**
- *
+ *
* {@link RestPermissionEvaluatorPlugin} class that evaluate ADMIN permissions over a Resource Policy
- *
+ *
* @author Mykhaylo Boychuk - (4Science.it)
*/
@Component
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/RestAuthenticationService.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/RestAuthenticationService.java
index 536ea2afc7..b1a47336ba 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/RestAuthenticationService.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/RestAuthenticationService.java
@@ -26,13 +26,14 @@ import org.springframework.stereotype.Service;
public interface RestAuthenticationService {
void addAuthenticationDataForUser(HttpServletRequest request, HttpServletResponse response,
- DSpaceAuthentication authentication) throws IOException;
+ DSpaceAuthentication authentication, boolean addCookie) throws IOException;
EPerson getAuthenticatedEPerson(HttpServletRequest request, Context context);
boolean hasAuthenticationData(HttpServletRequest request);
- void invalidateAuthenticationData(HttpServletRequest request, Context context) throws Exception;
+ void invalidateAuthenticationData(HttpServletRequest request, HttpServletResponse response, Context context)
+ throws Exception;
AuthenticationService getAuthenticationService();
@@ -44,4 +45,6 @@ public interface RestAuthenticationService {
*/
String getWwwAuthenticateHeaderValue(HttpServletRequest request, HttpServletResponse response);
+ void invalidateAuthenticationCookie(HttpServletResponse res);
+
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ShibbolethAuthenticationFilter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ShibbolethAuthenticationFilter.java
new file mode 100644
index 0000000000..736f2f48ab
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ShibbolethAuthenticationFilter.java
@@ -0,0 +1,53 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.security;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ * This class will filter shibboleth requests to try and authenticate them
+ *
+ * @author Giuseppe Digilio (giuseppe dot digilio at 4science dot it)
+ */
+public class ShibbolethAuthenticationFilter extends StatelessLoginFilter {
+
+ public ShibbolethAuthenticationFilter(String url, AuthenticationManager authenticationManager,
+ RestAuthenticationService restAuthenticationService) {
+ super(url, authenticationManager, restAuthenticationService);
+ }
+
+ @Override
+ public Authentication attemptAuthentication(HttpServletRequest req,
+ HttpServletResponse res) throws AuthenticationException {
+
+ return authenticationManager.authenticate(
+ new DSpaceAuthentication(null, null, new ArrayList<>())
+ );
+ }
+
+ @Override
+ protected void successfulAuthentication(HttpServletRequest req,
+ HttpServletResponse res,
+ FilterChain chain,
+ Authentication auth) throws IOException, ServletException {
+
+ DSpaceAuthentication dSpaceAuthentication = (DSpaceAuthentication) auth;
+ restAuthenticationService.addAuthenticationDataForUser(req, res, dSpaceAuthentication, true);
+ chain.doFilter(req, res);
+ }
+
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/StatelessAuthenticationFilter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/StatelessAuthenticationFilter.java
index 7c62a58620..eb6d2d6cc7 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/StatelessAuthenticationFilter.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/StatelessAuthenticationFilter.java
@@ -61,6 +61,7 @@ public class StatelessAuthenticationFilter extends BasicAuthenticationFilter {
Authentication authentication = getAuthentication(req);
if (authentication != null) {
SecurityContextHolder.getContext().setAuthentication(authentication);
+ restAuthenticationService.invalidateAuthenticationCookie(res);
}
chain.doFilter(req, res);
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/StatelessLoginFilter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/StatelessLoginFilter.java
index dca37edc1b..26add87923 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/StatelessLoginFilter.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/StatelessLoginFilter.java
@@ -28,9 +28,9 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
*/
public class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter {
- private AuthenticationManager authenticationManager;
+ protected AuthenticationManager authenticationManager;
- private RestAuthenticationService restAuthenticationService;
+ protected RestAuthenticationService restAuthenticationService;
@Override
public void afterPropertiesSet() {
@@ -63,7 +63,7 @@ public class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter
Authentication auth) throws IOException, ServletException {
DSpaceAuthentication dSpaceAuthentication = (DSpaceAuthentication) auth;
- restAuthenticationService.addAuthenticationDataForUser(req, res, dSpaceAuthentication);
+ restAuthenticationService.addAuthenticationDataForUser(req, res, dSpaceAuthentication, false);
}
@Override
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/VersionHistoryRestPermissionEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/VersionHistoryRestPermissionEvaluatorPlugin.java
new file mode 100644
index 0000000000..79586736b4
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/VersionHistoryRestPermissionEvaluatorPlugin.java
@@ -0,0 +1,69 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.security;
+
+import java.io.Serializable;
+import java.sql.SQLException;
+
+import org.apache.commons.lang3.StringUtils;
+import org.dspace.app.rest.model.VersionHistoryRest;
+import org.dspace.app.rest.utils.ContextUtil;
+import org.dspace.authorize.service.AuthorizeService;
+import org.dspace.core.Context;
+import org.dspace.services.ConfigurationService;
+import org.dspace.services.RequestService;
+import org.dspace.services.model.Request;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+
+/**
+ * This class acts as a PermissionEvaluator to decide whether a given request to a Versioning endpoint is allowed to
+ * pass through or not
+ */
+@Component
+public class VersionHistoryRestPermissionEvaluatorPlugin extends RestObjectPermissionEvaluatorPlugin {
+
+ private static final Logger log = LoggerFactory.getLogger(VersionHistoryRestPermissionEvaluatorPlugin.class);
+
+ @Autowired
+ private RequestService requestService;
+
+ @Autowired
+ private AuthorizeService authorizeService;
+
+ @Autowired
+ private ConfigurationService configurationService;
+
+
+ @Override
+ public boolean hasDSpacePermission(Authentication authentication, Serializable targetId, String targetType,
+ DSpaceRestPermission restPermission) {
+
+
+ if (!StringUtils.equalsIgnoreCase(targetType, VersionHistoryRest.NAME)) {
+ return false;
+ }
+
+ Request request = requestService.getCurrentRequest();
+ Context context = ContextUtil.obtainContext(request.getServletRequest());
+
+ try {
+ if (configurationService.getBooleanProperty("versioning.item.history.view.admin")
+ && !authorizeService.isAdmin(context)) {
+ return false;
+ }
+ return true;
+ } catch (SQLException e) {
+ log.error(e.getMessage(), e);
+ }
+ return false;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/VersionRestPermissionEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/VersionRestPermissionEvaluatorPlugin.java
new file mode 100644
index 0000000000..9b98718120
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/VersionRestPermissionEvaluatorPlugin.java
@@ -0,0 +1,83 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.security;
+
+import java.io.Serializable;
+import java.sql.SQLException;
+
+import org.apache.commons.lang3.StringUtils;
+import org.dspace.app.rest.model.VersionRest;
+import org.dspace.app.rest.utils.ContextUtil;
+import org.dspace.authorize.service.AuthorizeService;
+import org.dspace.core.Context;
+import org.dspace.services.ConfigurationService;
+import org.dspace.services.RequestService;
+import org.dspace.services.model.Request;
+import org.dspace.versioning.Version;
+import org.dspace.versioning.service.VersioningService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+
+/**
+ * This class acts as a PermissionEvaluator to decide whether a given request to a Versioning endpoint is allowed to
+ * pass through or not
+ */
+@Component
+public class VersionRestPermissionEvaluatorPlugin extends RestObjectPermissionEvaluatorPlugin {
+
+ private static final Logger log = LoggerFactory.getLogger(VersionRestPermissionEvaluatorPlugin.class);
+
+ @Autowired
+ private RequestService requestService;
+
+ @Autowired
+ private AuthorizeService authorizeService;
+
+ @Autowired
+ private VersioningService versioningService;
+
+ @Autowired
+ private ConfigurationService configurationService;
+
+
+ @Override
+ public boolean hasDSpacePermission(Authentication authentication, Serializable targetId, String targetType,
+ DSpaceRestPermission restPermission) {
+
+
+ if (!StringUtils.equalsIgnoreCase(targetType, VersionRest.NAME)) {
+ return false;
+ }
+
+ Request request = requestService.getCurrentRequest();
+ Context context = ContextUtil.obtainContext(request.getServletRequest());
+
+ try {
+ int versionId = Integer.parseInt(targetId.toString());
+ if (configurationService.getBooleanProperty("versioning.item.history.view.admin")
+ && !authorizeService.isAdmin(context)) {
+ return false;
+ }
+ Version version = versioningService.getVersion(context, versionId);
+ if (version == null) {
+ return true;
+ }
+ if (authorizeService.authorizeActionBoolean(context, version.getItem(),
+ restPermission.getDspaceApiActionId())) {
+ return true;
+ }
+
+ } catch (SQLException e) {
+ log.error(e.getMessage(), e);
+ }
+ return false;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WebSecurityConfiguration.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WebSecurityConfiguration.java
index 16aef3ffcd..32c0cdda00 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WebSecurityConfiguration.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WebSecurityConfiguration.java
@@ -109,6 +109,12 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
restAuthenticationService),
LogoutFilter.class)
+ //Add a filter before our shibboleth endpoints to do the authentication based on the data in the
+ // HTTP request
+ .addFilterBefore(new ShibbolethAuthenticationFilter("/api/authn/shibboleth", authenticationManager(),
+ restAuthenticationService),
+ LogoutFilter.class)
+
// Add a custom Token based authentication filter based on the token previously given to the client
// before each URL
.addFilterBefore(new StatelessAuthenticationFilter(authenticationManager(), restAuthenticationService,
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WorkspaceItemRestPermissionEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WorkspaceItemRestPermissionEvaluatorPlugin.java
new file mode 100644
index 0000000000..be4f2e74e7
--- /dev/null
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WorkspaceItemRestPermissionEvaluatorPlugin.java
@@ -0,0 +1,91 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest.security;
+
+import java.io.Serializable;
+import java.sql.SQLException;
+
+import org.apache.commons.lang3.StringUtils;
+import org.dspace.app.rest.model.WorkspaceItemRest;
+import org.dspace.app.rest.utils.ContextUtil;
+import org.dspace.content.WorkspaceItem;
+import org.dspace.content.service.WorkspaceItemService;
+import org.dspace.core.Context;
+import org.dspace.eperson.EPerson;
+import org.dspace.services.RequestService;
+import org.dspace.services.model.Request;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+
+/**
+ * {@link RestPermissionEvaluatorPlugin} class that evaluate READ, WRITE and DELETE permissions over a WorkspaceItem
+ *
+ * @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
+ */
+@Component
+public class WorkspaceItemRestPermissionEvaluatorPlugin extends RestObjectPermissionEvaluatorPlugin {
+
+ private static final Logger log = LoggerFactory.getLogger(WorkspaceItemRestPermissionEvaluatorPlugin.class);
+
+ @Autowired
+ private RequestService requestService;
+
+ @Autowired
+ WorkspaceItemService wis;
+
+ @Override
+ public boolean hasDSpacePermission(Authentication authentication, Serializable targetId, String targetType,
+ DSpaceRestPermission permission) {
+
+ DSpaceRestPermission restPermission = DSpaceRestPermission.convert(permission);
+ if (!DSpaceRestPermission.READ.equals(restPermission)
+ && !DSpaceRestPermission.WRITE.equals(restPermission)
+ && !DSpaceRestPermission.DELETE.equals(restPermission)) {
+ return false;
+ }
+ if (!StringUtils.equalsIgnoreCase(targetType, WorkspaceItemRest.NAME)) {
+ return false;
+ }
+
+ Request request = requestService.getCurrentRequest();
+ Context context = ContextUtil.obtainContext(request.getServletRequest());
+
+ EPerson ePerson = null;
+ WorkspaceItem witem = null;
+ try {
+ ePerson = context.getCurrentUser();
+ Integer dsoId = Integer.parseInt(targetId.toString());
+
+ // anonymous user
+ if (ePerson == null) {
+ return false;
+ }
+
+ witem = wis.find(context, dsoId);
+
+ // If the dso is null then we give permission so we can throw another status
+ // code instead
+ if (witem == null) {
+ return true;
+ }
+
+ if (witem.getSubmitter() != null) {
+ if (witem.getSubmitter().getID().equals(ePerson.getID())) {
+ return true;
+ }
+ }
+ } catch (SQLException e) {
+ log.error(e.getMessage(), e);
+ }
+
+ return false;
+ }
+}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/JWTTokenRestAuthenticationServiceImpl.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/JWTTokenRestAuthenticationServiceImpl.java
index 5e17a17701..3dbab09174 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/JWTTokenRestAuthenticationServiceImpl.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/JWTTokenRestAuthenticationServiceImpl.java
@@ -12,6 +12,7 @@ import java.sql.SQLException;
import java.text.ParseException;
import java.util.Iterator;
import java.util.List;
+import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -37,11 +38,13 @@ import org.springframework.stereotype.Component;
*
* @author Frederic Van Reet (frederic dot vanreet at atmire dot com)
* @author Tom Desair (tom dot desair at atmire dot com)
+ * @author Giuseppe Digilio (giuseppe dot digilio at 4science dot it)
*/
@Component
public class JWTTokenRestAuthenticationServiceImpl implements RestAuthenticationService, InitializingBean {
private static final Logger log = LoggerFactory.getLogger(RestAuthenticationService.class);
+ private static final String AUTHORIZATION_COOKIE = "Authorization-cookie";
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String AUTHORIZATION_TYPE = "Bearer";
@@ -61,7 +64,7 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
@Override
public void addAuthenticationDataForUser(HttpServletRequest request, HttpServletResponse response,
- DSpaceAuthentication authentication) throws IOException {
+ DSpaceAuthentication authentication, boolean addCookie) throws IOException {
try {
Context context = ContextUtil.obtainContext(request);
context.setCurrentUser(ePersonService.findByEmail(context, authentication.getName()));
@@ -71,7 +74,7 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
String token = jwtTokenHandler.createTokenForEPerson(context, request,
authentication.getPreviousLoginDate(), groups);
- addTokenToResponse(response, token);
+ addTokenToResponse(response, token, addCookie);
context.commit();
} catch (JOSEException e) {
@@ -99,15 +102,26 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
@Override
public boolean hasAuthenticationData(HttpServletRequest request) {
- return StringUtils.isNotBlank(request.getHeader(AUTHORIZATION_HEADER));
+ return StringUtils.isNotBlank(request.getHeader(AUTHORIZATION_HEADER))
+ || StringUtils.isNotBlank(getAuthorizationCookie(request));
}
@Override
- public void invalidateAuthenticationData(HttpServletRequest request, Context context) throws Exception {
+ public void invalidateAuthenticationData(HttpServletRequest request, HttpServletResponse response,
+ Context context) throws Exception {
String token = getToken(request);
+ invalidateAuthenticationCookie(response);
jwtTokenHandler.invalidateToken(token, request, context);
}
+ @Override
+ public void invalidateAuthenticationCookie(HttpServletResponse response) {
+ Cookie cookie = new Cookie(AUTHORIZATION_COOKIE, "");
+ cookie.setHttpOnly(true);
+ cookie.setMaxAge(0);
+ response.addCookie(cookie);
+ }
+
@Override
public AuthenticationService getAuthenticationService() {
return authenticationService;
@@ -140,18 +154,42 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
return wwwAuthenticate.toString();
}
- private void addTokenToResponse(final HttpServletResponse response, final String token) throws IOException {
+ private void addTokenToResponse(final HttpServletResponse response, final String token, final Boolean addCookie)
+ throws IOException {
+ // we need authentication cookies because Shibboleth can't use the authentication headers due to the redirects
+ if (addCookie) {
+ Cookie cookie = new Cookie(AUTHORIZATION_COOKIE, token);
+ cookie.setHttpOnly(true);
+ cookie.setSecure(true);
+ response.addCookie(cookie);
+ }
response.setHeader(AUTHORIZATION_HEADER, String.format("%s %s", AUTHORIZATION_TYPE, token));
}
private String getToken(HttpServletRequest request) {
+ String tokenValue = null;
String authHeader = request.getHeader(AUTHORIZATION_HEADER);
+ String authCookie = getAuthorizationCookie(request);
if (StringUtils.isNotBlank(authHeader)) {
- String tokenValue = authHeader.replace(AUTHORIZATION_TYPE, "").trim();
- return tokenValue;
- } else {
- return null;
+ tokenValue = authHeader.replace(AUTHORIZATION_TYPE, "").trim();
+ } else if (StringUtils.isNotBlank(authCookie)) {
+ tokenValue = authCookie;
}
+
+ return tokenValue;
+ }
+
+ private String getAuthorizationCookie(HttpServletRequest request) {
+ String authCookie = "";
+ Cookie[] cookies = request.getCookies();
+ if (cookies != null) {
+ for (Cookie cookie : cookies) {
+ if (cookie.getName().equals(AUTHORIZATION_COOKIE) && StringUtils.isNotEmpty(cookie.getValue())) {
+ authCookie = cookie.getValue();
+ }
+ }
+ }
+ return authCookie;
}
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java
index d8a86fa41a..2840035a7a 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java
@@ -8,7 +8,6 @@
package org.dspace.app.rest.submit;
import java.io.IOException;
-
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@@ -141,7 +140,7 @@ public class SubmissionService {
* Build the rest representation of a bitstream as used in the upload section
* ({@link DataUpload}. It contains all its metadata and the list of applied
* access conditions (@link {@link UploadBitstreamAccessConditionDTO}
- *
+ *
* @param configurationService the DSpace ConfigurationService
* @param source the bitstream to translate in its rest submission
* representation
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyAddPatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyAddPatchOperation.java
index 05e7c5052a..11413258fe 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyAddPatchOperation.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamResourcePolicyAddPatchOperation.java
@@ -8,7 +8,6 @@
package org.dspace.app.rest.submit.factory.impl;
import java.util.ArrayList;
-
import java.util.Date;
import java.util.List;
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/RegexUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/RegexUtils.java
index 65bd83c2aa..8e887261db 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/RegexUtils.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/RegexUtils.java
@@ -28,7 +28,7 @@ public class RegexUtils {
* identifier (digits or uuid)
*/
public static final String REGEX_REQUESTMAPPING_IDENTIFIER_AS_STRING_VERSION_STRONG = "/{id:^(?!^\\d+$)" +
- "(?!^[0-9a-fxA-FX]{8}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{12}$)[\\w+\\-]+$+}";
+ "(?!^[0-9a-fxA-FX]{8}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{12}$)[\\w+\\-\\.]+$+}";
/**
* Regular expression in the request mapping to accept number as identifier
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/Utils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/Utils.java
index 18392fc5a7..f241ba3b30 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/Utils.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/Utils.java
@@ -21,6 +21,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.SQLException;
@@ -55,6 +56,7 @@ import org.dspace.app.rest.model.ProcessRest;
import org.dspace.app.rest.model.ResourcePolicyRest;
import org.dspace.app.rest.model.RestAddressableModel;
import org.dspace.app.rest.model.RestModel;
+import org.dspace.app.rest.model.VersionHistoryRest;
import org.dspace.app.rest.model.hateoas.DSpaceResource;
import org.dspace.app.rest.model.hateoas.EmbeddedPage;
import org.dspace.app.rest.model.hateoas.HALResource;
@@ -64,18 +66,22 @@ import org.dspace.app.rest.projection.EmbedRelsProjection;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.repository.DSpaceRestRepository;
import org.dspace.app.rest.repository.LinkRestRepository;
+import org.dspace.app.rest.repository.ReloadableEntityObjectRepository;
import org.dspace.content.BitstreamFormat;
import org.dspace.content.DSpaceObject;
import org.dspace.content.service.BitstreamFormatService;
import org.dspace.content.service.DSpaceObjectService;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Context;
+import org.dspace.services.ConfigurationService;
import org.dspace.services.RequestService;
import org.dspace.util.UUIDUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
@@ -110,6 +116,10 @@ public class Utils {
@Autowired
RequestService requestService;
+ @Autowired
+ @Qualifier("defaultConversionService")
+ ConversionService conversionService;
+
@Autowired(required = true)
private List> dSpaceObjectServices;
@@ -119,6 +129,9 @@ public class Utils {
@Autowired
private ConverterService converter;
+ @Autowired
+ private ConfigurationService configurationService;
+
/** Cache to support fast lookups of LinkRest method annotation information. */
private Map> linkAnnotationForMethod = new HashMap<>();
@@ -168,12 +181,32 @@ public class Utils {
.withRel(rel);
}
+ /**
+ * Retrieve the {@link DSpaceRestRepository} for the specified category and model in the plural form as used in the endpoints.
+ * If the model is available in its singular form use {@link #getResourceRepositoryByCategoryAndModel(String, String)}
+ *
+ * @param apiCategory
+ * @param modelPlural
+ * @return
+ */
public DSpaceRestRepository getResourceRepository(String apiCategory, String modelPlural) {
String model = makeSingular(modelPlural);
+ return getResourceRepositoryByCategoryAndModel(apiCategory, model);
+ }
+
+ /**
+ * Retrieve the {@link DSpaceRestRepository} for the specified category and model. The model is in the singular form
+ * as returned by the {@link RestAddressableModel#getType()} method
+ *
+ * @param apiCategory
+ * @param modelSingular
+ * @return
+ */
+ public DSpaceRestRepository getResourceRepositoryByCategoryAndModel(String apiCategory, String modelSingular) {
try {
- return applicationContext.getBean(apiCategory + "." + model, DSpaceRestRepository.class);
+ return applicationContext.getBean(apiCategory + "." + modelSingular, DSpaceRestRepository.class);
} catch (NoSuchBeanDefinitionException e) {
- throw new RepositoryNotFoundException(apiCategory, model);
+ throw new RepositoryNotFoundException(apiCategory, modelSingular);
}
}
@@ -196,6 +229,9 @@ public class Utils {
if (StringUtils.equals(modelPlural, "processes")) {
return ProcessRest.NAME;
}
+ if (StringUtils.equals(modelPlural, "versionhistories")) {
+ return VersionHistoryRest.NAME;
+ }
return modelPlural.replaceAll("s$", "");
}
@@ -784,4 +820,89 @@ public class Utils {
}
return contentId;
}
+
+ /**
+ * Convert the input string in the primary key class according to the repository interface
+ *
+ * @param repository
+ * @param pkStr
+ * @return
+ */
+ public Serializable castToPKClass(ReloadableEntityObjectRepository repository, String pkStr) {
+ return (Serializable) conversionService.convert(pkStr, repository.getPKClass());
+ }
+
+ /**
+ * Return the dspace api model object corresponding to the provided, not null, rest object. This only works when the
+ * rest object is supported by a {@link DSpaceRestRepository} that also implement the
+ * {@link ReloadableEntityObjectRepository} interface. If this is not the case the method will throw an
+ * IllegalArgumentException
+ *
+ * @param context
+ * the DSpace Context
+ * @param restObj
+ * the not null rest object. If null the method will throws an {@link IllegalArgumentException}
+ * @return the dspace api model object corresponding to the provided, not null, rest object
+ * @throws IllegalArgumentException
+ * if the restObj is not supported by a {@link DSpaceRestRepository} that also implement the
+ * {@link ReloadableEntityObjectRepository} interface
+ * @throws SQLException
+ * if a database error occur
+ */
+ public Object getDSpaceAPIObjectFromRest(Context context, BaseObjectRest restObj)
+ throws IllegalArgumentException, SQLException {
+ DSpaceRestRepository repository = getResourceRepositoryByCategoryAndModel(restObj.getCategory(),
+ restObj.getType());
+ Serializable pk = castToPKClass((ReloadableEntityObjectRepository) repository, restObj.getId().toString());
+ return ((ReloadableEntityObjectRepository) repository).findDomainObjectByPk(context, pk);
+ }
+
+ /**
+ * Get the rest object associated with the specified URI
+ *
+ * @param context the DSpace context
+ * @param uri the uri of a {@link BaseObjectRest}
+ * @return the {@link BaseObjectRest} identified by the provided uri
+ * @throws SQLException if a database error occur
+ * @throws IllegalArgumentException if the uri is not valid
+ */
+ public BaseObjectRest getBaseObjectRestFromUri(Context context, String uri) throws SQLException {
+ String dspaceUrl = configurationService.getProperty("dspace.server.url");
+ // first check if the uri could be valid
+ if (!StringUtils.startsWith(uri, dspaceUrl)) {
+ throw new IllegalArgumentException("the supplied uri is not valid: " + uri);
+ }
+ // extract from the uri the category, model and id components
+ // they start after the dspaceUrl/api/{apiCategory}/{apiModel}/{id}
+ String[] uriParts = uri.substring(dspaceUrl.length() + (dspaceUrl.endsWith("/") ? 0 : 1) + "api/".length())
+ .split("/", 3);
+ if (uriParts.length != 3) {
+ throw new IllegalArgumentException("the supplied uri is not valid: " + uri);
+ }
+
+ DSpaceRestRepository repository;
+ try {
+ repository = getResourceRepository(uriParts[0], uriParts[1]);
+ if (!(repository instanceof ReloadableEntityObjectRepository)) {
+ throw new IllegalArgumentException("the supplied uri is not valid: " + uri);
+ }
+ } catch (RepositoryNotFoundException e) {
+ throw new IllegalArgumentException("the supplied uri is not valid: " + uri, e);
+ }
+
+ Serializable pk;
+ try {
+ // cast the string id in the uriParts to the real pk class
+ pk = castToPKClass((ReloadableEntityObjectRepository) repository, uriParts[2]);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("the supplied uri is not valid: " + uri, e);
+ }
+ try {
+ // disable the security as we only need to retrieve the object to further process the authorization
+ context.turnOffAuthorisationSystem();
+ return (BaseObjectRest) repository.findOne(context, pk);
+ } finally {
+ context.restoreAuthSystemState();
+ }
+ }
}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/oai/OAIpmhIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/oai/OAIpmhIT.java
index 08bb69543a..3b277a937f 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/oai/OAIpmhIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/oai/OAIpmhIT.java
@@ -28,7 +28,6 @@ import com.lyncode.xoai.dataprovider.services.api.ResourceResolver;
import com.lyncode.xoai.dataprovider.services.impl.BaseDateProvider;
import com.lyncode.xoai.dataprovider.xml.xoaiconfig.Configuration;
import com.lyncode.xoai.dataprovider.xml.xoaiconfig.ContextConfiguration;
-
import org.apache.commons.lang3.time.DateUtils;
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
@@ -39,7 +38,6 @@ import org.dspace.xoai.services.api.EarliestDateResolver;
import org.dspace.xoai.services.api.cache.XOAICacheService;
import org.dspace.xoai.services.api.config.XOAIManagerResolver;
import org.dspace.xoai.services.api.xoai.DSpaceFilterResolver;
-
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/opensearch/OpenSearchControllerDisabledIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/opensearch/OpenSearchControllerDisabledIT.java
index 7a0b1c9c8d..a757ecbca3 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/opensearch/OpenSearchControllerDisabledIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/opensearch/OpenSearchControllerDisabledIT.java
@@ -12,10 +12,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
-
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
-
import org.junit.Before;
import org.junit.Test;
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/opensearch/OpenSearchControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/opensearch/OpenSearchControllerIT.java
index 9d7bf15867..58cd3d0a16 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/opensearch/OpenSearchControllerIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/opensearch/OpenSearchControllerIT.java
@@ -15,13 +15,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.builder.ItemBuilder;
-
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
-
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
-
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.junit.Before;
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthenticationRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthenticationRestControllerIT.java
index 01d1c527de..d9fb29926e 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthenticationRestControllerIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthenticationRestControllerIT.java
@@ -21,6 +21,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.Base64;
+import javax.servlet.http.Cookie;
import org.dspace.app.rest.builder.GroupBuilder;
import org.dspace.app.rest.matcher.AuthenticationStatusMatcher;
@@ -40,6 +41,7 @@ import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
*
* @author Frederic Van Reet (frederic dot vanreet at atmire dot com)
* @author Tom Desair (tom dot desair at atmire dot com)
+ * @author Giuseppe Digilio (giuseppe dot digilio at 4science dot it)
*/
public class AuthenticationRestControllerIT extends AbstractControllerIntegrationTest {
@@ -48,6 +50,9 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
public static final String[] PASS_ONLY = {"org.dspace.authenticate.PasswordAuthentication"};
public static final String[] SHIB_ONLY = {"org.dspace.authenticate.ShibAuthentication"};
+ public static final String[] SHIB_AND_PASS =
+ {"org.dspace.authenticate.ShibAuthentication",
+ "org.dspace.authenticate.PasswordAuthentication"};
public static final String[] SHIB_AND_IP =
{"org.dspace.authenticate.IPAuthentication",
"org.dspace.authenticate.ShibAuthentication"};
@@ -97,7 +102,43 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(false)))
- .andExpect(jsonPath("$.type", is("status")));
+ .andExpect(jsonPath("$.type", is("status")))
+ .andExpect(header().string("WWW-Authenticate",
+ "password realm=\"DSpace REST API\""));
+ }
+
+ @Test
+ public void testStatusAuthenticatedWithCookie() throws Exception {
+ context.turnOffAuthorisationSystem();
+ //Enable Shibboleth login
+ configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
+
+ context.restoreAuthSystemState();
+
+ //Simulate that a shibboleth authentication has happened
+ String token = getClient().perform(post("/api/authn/login")
+ .requestAttr("SHIB-MAIL", eperson.getEmail())
+ .requestAttr("SHIB-SCOPED-AFFILIATION", "faculty;staff"))
+ .andExpect(status().isOk())
+ .andReturn().getResponse().getHeader(AUTHORIZATION_HEADER).replace("Bearer ", "");
+
+ Cookie[] cookies = new Cookie[1];
+ cookies[0] = new Cookie(AUTHORIZATION_COOKIE, token);
+
+ //Check if we are authenticated with a status request with authorization cookie
+ getClient().perform(get("/api/authn/status")
+ .secure(true)
+ .cookie(cookies))
+ .andExpect(status().isOk())
+ //We expect the content type to be "application/hal+json;charset=UTF-8"
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$.okay", is(true)))
+ .andExpect(jsonPath("$.authenticated", is(true)))
+ .andExpect(jsonPath("$.type", is("status")));
+
+ //Logout
+ getClient(token).perform(get("/api/authn/logout"))
+ .andExpect(status().isNoContent());
}
@Test
@@ -354,6 +395,108 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(status().isMethodNotAllowed());
}
+ @Test
+ public void testShibbolethLoginURLWithDefaultLazyURL() throws Exception {
+ context.turnOffAuthorisationSystem();
+ //Enable Shibboleth login
+ configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
+
+ //Create a reviewers group
+ Group reviewersGroup = GroupBuilder.createGroup(context)
+ .withName("Reviewers")
+ .build();
+
+ //Faculty members are assigned to the Reviewers group
+ configurationService.setProperty("authentication-shibboleth.role.faculty", "Reviewers");
+ context.restoreAuthSystemState();
+
+ getClient().perform(post("/api/authn/login").header("Referer", "http://my.uni.edu"))
+ .andExpect(status().isUnauthorized())
+ .andExpect(header().string("WWW-Authenticate",
+ "shibboleth realm=\"DSpace REST API\", " +
+ "location=\"https://localhost/Shibboleth.sso/Login?" +
+ "target=http%3A%2F%2Flocalhost%2Fapi%2Fauthn%2Fshibboleth%3F" +
+ "redirectUrl%3Dhttp%3A%2F%2Fmy.uni.edu\""));
+ }
+
+ @Test
+ public void testShibbolethLoginURLWithServerlURLConteiningPort() throws Exception {
+ context.turnOffAuthorisationSystem();
+ //Enable Shibboleth login
+ configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
+ configurationService.setProperty("dspace.server.url", "http://localhost:8080/server");
+ configurationService.setProperty("authentication-shibboleth.lazysession.secure", false);
+
+ //Create a reviewers group
+ Group reviewersGroup = GroupBuilder.createGroup(context)
+ .withName("Reviewers")
+ .build();
+
+ //Faculty members are assigned to the Reviewers group
+ configurationService.setProperty("authentication-shibboleth.role.faculty", "Reviewers");
+ context.restoreAuthSystemState();
+
+ getClient().perform(post("/api/authn/login").header("Referer", "http://my.uni.edu"))
+ .andExpect(status().isUnauthorized())
+ .andExpect(header().string("WWW-Authenticate",
+ "shibboleth realm=\"DSpace REST API\", " +
+ "location=\"http://localhost:8080/Shibboleth.sso/Login?" +
+ "target=http%3A%2F%2Flocalhost%3A8080%2Fserver%2Fapi%2Fauthn%2Fshibboleth%3F" +
+ "redirectUrl%3Dhttp%3A%2F%2Fmy.uni.edu\""));
+ }
+
+ @Test
+ public void testShibbolethLoginURLWithConfiguredLazyURL() throws Exception {
+ context.turnOffAuthorisationSystem();
+ //Enable Shibboleth login
+ configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
+ configurationService.setProperty("authentication-shibboleth.lazysession.loginurl",
+ "http://shibboleth.org/Shibboleth.sso/Login");
+
+ //Create a reviewers group
+ Group reviewersGroup = GroupBuilder.createGroup(context)
+ .withName("Reviewers")
+ .build();
+
+ //Faculty members are assigned to the Reviewers group
+ configurationService.setProperty("authentication-shibboleth.role.faculty", "Reviewers");
+ context.restoreAuthSystemState();
+
+ getClient().perform(post("/api/authn/login").header("Referer", "http://my.uni.edu"))
+ .andExpect(status().isUnauthorized())
+ .andExpect(header().string("WWW-Authenticate",
+ "shibboleth realm=\"DSpace REST API\", " +
+ "location=\"http://shibboleth.org/Shibboleth.sso/Login?" +
+ "target=http%3A%2F%2Flocalhost%2Fapi%2Fauthn%2Fshibboleth%3F" +
+ "redirectUrl%3Dhttp%3A%2F%2Fmy.uni.edu\""));
+ }
+
+ @Test
+ public void testShibbolethLoginURLWithConfiguredLazyURLWithPort() throws Exception {
+ context.turnOffAuthorisationSystem();
+ //Enable Shibboleth login
+ configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
+ configurationService.setProperty("authentication-shibboleth.lazysession.loginurl",
+ "http://shibboleth.org:8080/Shibboleth.sso/Login");
+
+ //Create a reviewers group
+ Group reviewersGroup = GroupBuilder.createGroup(context)
+ .withName("Reviewers")
+ .build();
+
+ //Faculty members are assigned to the Reviewers group
+ configurationService.setProperty("authentication-shibboleth.role.faculty", "Reviewers");
+ context.restoreAuthSystemState();
+
+ getClient().perform(post("/api/authn/login").header("Referer", "http://my.uni.edu"))
+ .andExpect(status().isUnauthorized())
+ .andExpect(header().string("WWW-Authenticate",
+ "shibboleth realm=\"DSpace REST API\", " +
+ "location=\"http://shibboleth.org:8080/Shibboleth.sso/Login?" +
+ "target=http%3A%2F%2Flocalhost%2Fapi%2Fauthn%2Fshibboleth%3F" +
+ "redirectUrl%3Dhttp%3A%2F%2Fmy.uni.edu\""));
+ }
+
@Test
@Ignore
// Ignored until an endpoint is added to return all groups
@@ -375,7 +518,9 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(status().isUnauthorized())
.andExpect(header().string("WWW-Authenticate",
"shibboleth realm=\"DSpace REST API\", " +
- "location=\"/Shibboleth.sso/Login?target=http%3A%2F%2Fmy.uni.edu\""));
+ "location=\"https://localhost/Shibboleth.sso/Login?" +
+ "target=http%3A%2F%2Flocalhost%2Fapi%2Fauthn%2Fshibboleth%3F" +
+ "redirectUrl%3Dhttp%3A%2F%2Fmy.uni.edu\""));
//Simulate that a shibboleth authentication has happened
@@ -411,7 +556,9 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(status().isUnauthorized())
.andExpect(header().string("WWW-Authenticate",
"ip realm=\"DSpace REST API\", shibboleth realm=\"DSpace REST API\", " +
- "location=\"/Shibboleth.sso/Login?target=http%3A%2F%2Fmy.uni.edu\""));
+ "location=\"https://localhost/Shibboleth.sso/Login?" +
+ "target=http%3A%2F%2Flocalhost%2Fapi%2Fauthn%2Fshibboleth%3F" +
+ "redirectUrl%3Dhttp%3A%2F%2Fmy.uni.edu\""));
//Simulate that a shibboleth authentication has happened
String token = getClient().perform(post("/api/authn/login")
@@ -454,4 +601,164 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
EPersonMatcher.matchEPersonWithGroups(eperson.getEmail(), "Anonymous")));
}
+ @Test
+ public void testShibbolethAndPasswordAuthentication() throws Exception {
+ context.turnOffAuthorisationSystem();
+ //Enable Shibboleth and password login
+ configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_AND_PASS);
+
+ context.restoreAuthSystemState();
+
+ //Check if WWW-Authenticate header contains shibboleth and password
+ getClient().perform(get("/api/authn/status").header("Referer", "http://my.uni.edu"))
+ .andExpect(status().isOk())
+ .andExpect(header().string("WWW-Authenticate",
+ "shibboleth realm=\"DSpace REST API\", " +
+ "location=\"https://localhost/Shibboleth.sso/Login?" +
+ "target=http%3A%2F%2Flocalhost%2Fapi%2Fauthn%2Fshibboleth%3F" +
+ "redirectUrl%3Dhttp%3A%2F%2Fmy.uni.edu\"" +
+ ", password realm=\"DSpace REST API\""));
+
+ //Simulate a password authentication
+ String token = getAuthToken(eperson.getEmail(), password);
+
+ //Check if we have a valid token
+ getClient(token).perform(get("/api/authn/status"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.okay", is(true)))
+ .andExpect(jsonPath("$.authenticated", is(true)))
+ .andExpect(jsonPath("$.type", is("status")));
+
+ //Logout
+ getClient(token).perform(get("/api/authn/logout"))
+ .andExpect(status().isNoContent());
+
+ //Check if we are actually logged out
+ getClient(token).perform(get("/api/authn/status"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.okay", is(true)))
+ .andExpect(jsonPath("$.authenticated", is(false)))
+ .andExpect(jsonPath("$.type", is("status")));
+
+ //Simulate that a shibboleth authentication has happened
+ token = getClient().perform(post("/api/authn/login")
+ .requestAttr("SHIB-MAIL", eperson.getEmail())
+ .requestAttr("SHIB-SCOPED-AFFILIATION", "faculty;staff"))
+ .andExpect(status().isOk())
+ .andReturn().getResponse().getHeader(AUTHORIZATION_HEADER).replace("Bearer ", "");
+
+ //Check if we have a valid token
+ getClient(token).perform(get("/api/authn/status"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.okay", is(true)))
+ .andExpect(jsonPath("$.authenticated", is(true)))
+ .andExpect(jsonPath("$.type", is("status")));
+
+ //Logout
+ getClient(token).perform(get("/api/authn/logout"))
+ .andExpect(status().isNoContent());
+
+ }
+
+ @Test
+ public void testOnlyPasswordAuthenticationWorks() throws Exception {
+ context.turnOffAuthorisationSystem();
+ //Enable only password login
+ configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", PASS_ONLY);
+
+ context.restoreAuthSystemState();
+
+ //Check if WWW-Authenticate header contains only
+ getClient().perform(get("/api/authn/status").header("Referer", "http://my.uni.edu"))
+ .andExpect(status().isOk())
+ .andExpect(header().string("WWW-Authenticate",
+ "password realm=\"DSpace REST API\""));
+
+ //Simulate a password authentication
+ String token = getAuthToken(eperson.getEmail(), password);
+
+ //Check if we have a valid token
+ getClient(token).perform(get("/api/authn/status"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.okay", is(true)))
+ .andExpect(jsonPath("$.authenticated", is(true)))
+ .andExpect(jsonPath("$.type", is("status")));
+
+ //Logout
+ getClient(token).perform(get("/api/authn/logout"))
+ .andExpect(status().isNoContent());
+ }
+
+ @Test
+ public void testShibbolethAuthenticationDoesNotWorkWithPassOnly() throws Exception {
+ context.turnOffAuthorisationSystem();
+ //Enable only password login
+ configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", PASS_ONLY);
+
+ context.restoreAuthSystemState();
+
+ //Check if WWW-Authenticate header contains only password
+ getClient().perform(get("/api/authn/status").header("Referer", "http://my.uni.edu"))
+ .andExpect(status().isOk())
+ .andExpect(header().string("WWW-Authenticate",
+ "password realm=\"DSpace REST API\""));
+
+ //Check if a shibboleth authentication fails
+ getClient().perform(post("/api/authn/login")
+ .requestAttr("SHIB-MAIL", eperson.getEmail())
+ .requestAttr("SHIB-SCOPED-AFFILIATION", "faculty;staff"))
+ .andExpect(status().isUnauthorized());
+
+ }
+
+ @Test
+ public void testOnlyShibbolethAuthenticationWorks() throws Exception {
+ context.turnOffAuthorisationSystem();
+ //Enable only Shibboleth login
+ configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
+
+ context.restoreAuthSystemState();
+
+ //Check if WWW-Authenticate header contains only shibboleth
+ getClient().perform(get("/api/authn/status").header("Referer", "http://my.uni.edu"))
+ .andExpect(status().isOk())
+ .andExpect(header().string("WWW-Authenticate",
+ "shibboleth realm=\"DSpace REST API\", " +
+ "location=\"https://localhost/Shibboleth.sso/Login?" +
+ "target=http%3A%2F%2Flocalhost%2Fapi%2Fauthn%2Fshibboleth%3F" +
+ "redirectUrl%3Dhttp%3A%2F%2Fmy.uni.edu\""));
+
+ //Simulate that a shibboleth authentication has happened
+ String token = getClient().perform(post("/api/authn/login")
+ .requestAttr("SHIB-MAIL", eperson.getEmail())
+ .requestAttr("SHIB-SCOPED-AFFILIATION", "faculty;staff"))
+ .andExpect(status().isOk())
+ .andReturn().getResponse().getHeader(AUTHORIZATION_HEADER);
+
+ //Logout
+ getClient(token).perform(get("/api/authn/logout"))
+ .andExpect(status().isNoContent());
+ }
+
+ @Test
+ public void testPasswordAuthenticationDoesNotWorkWithShibOnly() throws Exception {
+ context.turnOffAuthorisationSystem();
+ //Enable only Shibboleth login
+ configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
+
+ //Create a reviewers group
+ Group reviewersGroup = GroupBuilder.createGroup(context)
+ .withName("Reviewers")
+ .build();
+
+ //Faculty members are assigned to the Reviewers group
+ configurationService.setProperty("authentication-shibboleth.role.faculty", "Reviewers");
+ context.restoreAuthSystemState();
+
+ getClient().perform(post("/api/authn/login")
+ .param("user", eperson.getEmail())
+ .param("password", password))
+ .andExpect(status().isUnauthorized());
+
+ }
}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthorizationFeatureRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthorizationFeatureRestRepositoryIT.java
new file mode 100644
index 0000000000..a3556ad503
--- /dev/null
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthorizationFeatureRestRepositoryIT.java
@@ -0,0 +1,173 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest;
+
+import static com.jayway.jsonpath.JsonPath.read;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.fail;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.jayway.jsonpath.matchers.JsonPathMatchers;
+import org.dspace.app.rest.authorization.AlwaysTrueFeature;
+import org.dspace.app.rest.authorization.AuthorizationFeature;
+import org.dspace.app.rest.authorization.AuthorizationFeatureService;
+import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
+import org.hamcrest.Matchers;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Test suite for the Authorization Feature endpoint
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ *
+ */
+public class AuthorizationFeatureRestRepositoryIT extends AbstractControllerIntegrationTest {
+ @Autowired
+ private AuthorizationFeatureService authzFeatureService;
+
+ @Test
+ /**
+ * All the features should be returned
+ *
+ * @throws Exception
+ */
+ public void findAllTest() throws Exception {
+ int featuresNum = authzFeatureService.findAll().size();
+ int expReturn = featuresNum > 20 ? 20 : featuresNum;
+ String adminToken = getAuthToken(admin.getEmail(), password);
+
+ // verify that only the admin can access the endpoint (see subsequent call in the method)
+ getClient(adminToken).perform(get("/api/authz/features")).andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.features", Matchers.hasSize(is(expReturn))))
+ .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/authz/features")))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", is(featuresNum)));
+ // verify that anonymous user cannot access
+ getClient().perform(get("/api/authz/features")).andExpect(status().isUnauthorized());
+ // verify that normal user cannot access
+ String epersonAuthToken = getAuthToken(eperson.getEmail(), password);
+ getClient(epersonAuthToken).perform(get("/api/authz/features")).andExpect(status().isForbidden());
+
+ }
+
+ @Test
+ /**
+ * The feature endpoint must provide proper pagination. Unauthorized and
+ * forbidden scenarios are managed in the findAllTest
+ *
+ * @throws Exception
+ */
+ public void findAllWithPaginationTest() throws Exception {
+ int featuresNum = authzFeatureService.findAll().size();
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ List featureIDs = new ArrayList();
+ for (int page = 0; page < featuresNum; page++) {
+ AtomicReference idRef = new AtomicReference();
+
+ getClient(adminToken)
+ .perform(get("/api/authz/features").param("page", String.valueOf(page)).param("size", "1"))
+ .andExpect(status().isOk()).andExpect(jsonPath("$._embedded.features", Matchers.hasSize(is(1))))
+ .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/authz/features")))
+ .andExpect(
+ (page == 0) ? jsonPath("$._links.prev.href").doesNotExist()
+ : jsonPath("$._links.prev.href", Matchers.containsString("/api/authz/features")))
+ .andExpect((page == featuresNum - 1)
+ ? jsonPath("$._links.next.href").doesNotExist()
+ : jsonPath("$._links.next.href", Matchers.containsString("/api/authz/features")))
+ .andExpect(jsonPath("$._links.first.href", Matchers.containsString("/api/authz/features")))
+ .andExpect(jsonPath("$._links.last.href", Matchers.containsString("/api/authz/features")))
+ .andExpect(jsonPath("$.page.size", is(1)))
+ .andExpect(jsonPath("$.page.totalElements", is(Integer.valueOf(featuresNum))))
+ .andDo(result -> idRef
+ .set(read(result.getResponse().getContentAsString(), "$._embedded.features[0].id")));
+
+ if (idRef.get() == null || featureIDs.contains(idRef.get())) {
+ fail("Duplicate feature " + idRef.get() + " returned at page " + page);
+ }
+ featureIDs.add(idRef.get());
+ }
+ }
+
+ @Test
+ /**
+ * The feature resource endpoint must expose the proper structure and be reserved to administrators
+ *
+ * @throws Exception
+ */
+ public void findOneTest() throws Exception {
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ // verify that only the admin can access the endpoint (see subsequent call in the method)
+ getClient(adminToken).perform(get("/api/authz/features/withdrawItem")).andExpect(status().isOk())
+ .andExpect(jsonPath("$.id", is("withdrawItem")))
+ .andExpect(jsonPath("$.description", Matchers.any(String.class)))
+ .andExpect(jsonPath("$.resourcetypes", Matchers.contains("core.item")))
+ .andExpect(jsonPath("$.type", is("feature")));
+ // verify that anonymous user cannot access
+ getClient().perform(get("/api/authz/features/withdrawItem")).andExpect(status().isUnauthorized());
+ // verify that normal user cannot access
+ String epersonAuthToken = getAuthToken(eperson.getEmail(), password);
+ getClient(epersonAuthToken).perform(get("/api/authz/features/withdrawItem")).andExpect(status().isForbidden());
+ }
+
+ @Test
+ public void findOneNotFoundTest() throws Exception {
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ // verify that only the admin can access the endpoint and get the not found response code
+ // (see subsequent calls in the method for unauthorized and forbidden attempts)
+ getClient(adminToken).perform(get("/api/authz/features/not-existing-feature")).andExpect(status().isNotFound());
+ // verify that anonymous user cannot access, without information disclosure
+ getClient().perform(get("/api/authz/features/not-existing-feature")).andExpect(status().isUnauthorized());
+ // verify that normal user cannot access, without information disclosure
+ getClient(adminToken).perform(get("/api/authz/features/1")).andExpect(status().isNotFound());
+ }
+
+ @Test
+ /**
+ * It should be possible to find features by resourcetype. The endpoint is only available to administrators
+ *
+ * @throws Exception
+ */
+ public void findByResourceTypeTest() throws Exception {
+ AuthorizationFeature alwaysTrueFeature = authzFeatureService.find(AlwaysTrueFeature.NAME);
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ for (String type : alwaysTrueFeature.getSupportedTypes()) {
+ // verify that only the admin can access the endpoint (see subsequent call in the method)
+ getClient(adminToken).perform(get("/api/authz/features/search/resourcetype").param("type", type))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",
+ JsonPathMatchers.hasJsonPath("$._embedded.features",
+ Matchers.everyItem(
+ JsonPathMatchers.hasJsonPath("$.resourcetypes",
+ Matchers.hasItem(is(type))))
+ )))
+ .andExpect(
+ jsonPath("$._links.self.href",
+ Matchers.containsString("/api/authz/features/search/resourcetype")));
+ }
+ // verify that the right response code is returned also for not existing types
+ getClient(adminToken).perform(get("/api/authz/features/search/resourcetype").param("type", "NOT-EXISTING"))
+ .andExpect(status().isOk()).andExpect(jsonPath("$.page.totalElements", is(0)));
+ // verify that anonymous user cannot access, without information disclosure
+ getClient().perform(get("/api/authz/features/search/resourcetype").param("type", "core.item"))
+ .andExpect(status().isUnauthorized());
+ // verify that normal user cannot access, without information disclosure
+ String epersonAuthToken = getAuthToken(eperson.getEmail(), password);
+ getClient(epersonAuthToken).perform(get("/api/authz/features/search/resourcetype").param("type", "core.item"))
+ .andExpect(status().isForbidden());
+
+ }
+
+}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthorizationFeatureServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthorizationFeatureServiceIT.java
new file mode 100644
index 0000000000..58b5d1a730
--- /dev/null
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthorizationFeatureServiceIT.java
@@ -0,0 +1,173 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.junit.Assert.assertThat;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.dspace.app.rest.authorization.AlwaysFalseFeature;
+import org.dspace.app.rest.authorization.AlwaysThrowExceptionFeature;
+import org.dspace.app.rest.authorization.AlwaysTrueFeature;
+import org.dspace.app.rest.authorization.AuthorizationFeature;
+import org.dspace.app.rest.authorization.AuthorizationFeatureService;
+import org.dspace.app.rest.authorization.TrueForAdminsFeature;
+import org.dspace.app.rest.converter.ConverterService;
+import org.dspace.app.rest.model.CollectionRest;
+import org.dspace.app.rest.model.SiteRest;
+import org.dspace.app.rest.projection.DefaultProjection;
+import org.dspace.app.rest.test.AbstractIntegrationTestWithDatabase;
+import org.dspace.app.rest.utils.DSpaceConfigurationInitializer;
+import org.dspace.app.rest.utils.DSpaceKernelInitializer;
+import org.dspace.content.Site;
+import org.dspace.content.service.SiteService;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+/**
+ * Test for the Authorization Feature Service
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ *
+ */
+//Run tests with JUnit 4 and Spring TestContext Framework
+@RunWith(SpringRunner.class)
+//Specify main class to use to load Spring ApplicationContext
+//NOTE: By default, Spring caches and reuses ApplicationContext for each integration test (to speed up tests)
+//See: https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#integration-testing
+@SpringBootTest(classes = Application.class)
+//Load DSpace initializers in Spring ApplicationContext (to initialize DSpace Kernel & Configuration)
+@ContextConfiguration(initializers = { DSpaceKernelInitializer.class, DSpaceConfigurationInitializer.class })
+//Tell Spring to make ApplicationContext an instance of WebApplicationContext (for web-based tests)
+@WebAppConfiguration
+public class AuthorizationFeatureServiceIT extends AbstractIntegrationTestWithDatabase {
+ @Autowired
+ private SiteService siteService;
+
+ @Autowired
+ private ConverterService converterService;
+
+ @Autowired
+ private AuthorizationFeatureService authzFeatureService;
+
+ @Test
+ /**
+ * All the features available in the Sprint Context should be returned
+ *
+ * @throws Exception
+ */
+ public void findAllTest() throws Exception {
+ List authzFeatureServiceFindAll = authzFeatureService.findAll();
+
+ assertThat("We have at least our 7 mock features for testing",
+ authzFeatureServiceFindAll.size(), greaterThanOrEqualTo(7));
+
+ Set featureNames = new HashSet();
+ for (AuthorizationFeature f : authzFeatureServiceFindAll) {
+ featureNames.add(f.getName());
+ }
+
+ assertThat("all the features must have unique name", authzFeatureServiceFindAll.size(),
+ equalTo(featureNames.size()));
+ }
+
+ @Test
+ /**
+ * The find method should return existing feature and null in the case the feature doesn't exist
+ *
+ * @throws Exception
+ */
+ public void findTest() throws Exception {
+ AuthorizationFeature aFeature = authzFeatureService.find(AlwaysTrueFeature.NAME);
+ assertThat("check that one of our mock feature is retrieved", aFeature.getName(),
+ equalTo(AlwaysTrueFeature.NAME));
+
+ AuthorizationFeature aNotExistingFeature = authzFeatureService.find("this feature doesn't exist!");
+ assertThat("check that not existing feature name return null", aNotExistingFeature, equalTo(null));
+ }
+
+ @Test
+ /**
+ * The findByResourceType must return only features that support the specified type
+ *
+ * @throws Exception
+ */
+ public void findByResourceTypeTest() throws Exception {
+ // we have at least one feature that support the Site object
+ final String siteUniqueType = SiteRest.CATEGORY + "." + SiteRest.NAME;
+ List siteFeatures = authzFeatureService.findByResourceType(siteUniqueType);
+ assertThat(siteFeatures.size(), greaterThan(0));
+ boolean alwaysTrueFound = false;
+ for (AuthorizationFeature f : siteFeatures) {
+ assertThat(ArrayUtils.contains(f.getSupportedTypes(), siteUniqueType), equalTo(true));
+ alwaysTrueFound = alwaysTrueFound || AlwaysTrueFeature.NAME.equals(f.getName());
+ }
+ assertThat(alwaysTrueFound, equalTo(true));
+
+ // we can check that the AlwaysTrueFeature is returned also when searching for a
+ // type other than the Site (that is the first type supported by the feature)
+ alwaysTrueFound = false;
+ final String collectionUniqueType = CollectionRest.CATEGORY + "." + CollectionRest.NAME;
+ List collectionFeatures = authzFeatureService.findByResourceType(collectionUniqueType);
+ for (AuthorizationFeature f : collectionFeatures) {
+ assertThat(ArrayUtils.contains(f.getSupportedTypes(), collectionUniqueType), equalTo(true));
+ alwaysTrueFound = alwaysTrueFound || AlwaysTrueFeature.NAME.equals(f.getName());
+ }
+
+ // finally check that not existing type will return an empty list
+ final List notExistingTypeFeatures = authzFeatureService
+ .findByResourceType("NOT-EXISTING-TYPE");
+ assertThat(notExistingTypeFeatures.size(), equalTo(0));
+ }
+
+ @Test
+ /**
+ * The isAuthorized must return true for authorized feature and false for not authorized feature
+ *
+ * @throws Exception
+ */
+ public void isAuthorizedTest() throws Exception {
+ Site site = siteService.findSite(context);
+ SiteRest siteRest = converterService.toRest(site, DefaultProjection.DEFAULT);
+
+ AuthorizationFeature alwaysTrue = authzFeatureService.find(AlwaysTrueFeature.NAME);
+ AuthorizationFeature alwaysFalse = authzFeatureService.find(AlwaysFalseFeature.NAME);
+ AuthorizationFeature alwaysThrowEx = authzFeatureService.find(AlwaysThrowExceptionFeature.NAME);
+ AuthorizationFeature trueForAdmins = authzFeatureService.find(TrueForAdminsFeature.NAME);
+
+ assertThat(authzFeatureService.isAuthorized(context, alwaysTrue, siteRest), equalTo(true));
+ assertThat(authzFeatureService.isAuthorized(context, alwaysFalse, siteRest), equalTo(false));
+ try {
+ authzFeatureService.isAuthorized(context, alwaysThrowEx, siteRest);
+ // this code should be not run as the previous one throw an exception that we expect to be re-thrown
+ assertThat("the exception has been not re-thrown!", false, equalTo(true));
+ } catch (Exception ex) {
+ // if this code is executed the exception was re-thrown
+ assertThat("exceptions are rethrown", true, equalTo(true));
+ }
+ assertThat(authzFeatureService.isAuthorized(context, trueForAdmins, siteRest), equalTo(false));
+ // login our admin
+ context.setCurrentUser(admin);
+ assertThat(authzFeatureService.isAuthorized(context, trueForAdmins, siteRest), equalTo(true));
+ // finally check that a null object will always result in false to be returned
+ assertThat(authzFeatureService.isAuthorized(context, alwaysTrue, null), equalTo(false));
+ // without call at all the authorizationFeature to prevent NPE
+ assertThat(authzFeatureService.isAuthorized(context, alwaysThrowEx, null), equalTo(false));
+ }
+
+}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthorizationRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthorizationRestRepositoryIT.java
new file mode 100644
index 0000000000..a73a3aabc6
--- /dev/null
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthorizationRestRepositoryIT.java
@@ -0,0 +1,1328 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest;
+
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.is;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import java.io.Serializable;
+import java.util.UUID;
+
+import com.jayway.jsonpath.matchers.JsonPathMatchers;
+import org.apache.logging.log4j.Logger;
+import org.dspace.app.rest.authorization.AlwaysFalseFeature;
+import org.dspace.app.rest.authorization.AlwaysThrowExceptionFeature;
+import org.dspace.app.rest.authorization.AlwaysTrueFeature;
+import org.dspace.app.rest.authorization.Authorization;
+import org.dspace.app.rest.authorization.AuthorizationFeature;
+import org.dspace.app.rest.authorization.AuthorizationFeatureService;
+import org.dspace.app.rest.authorization.TrueForAdminsFeature;
+import org.dspace.app.rest.authorization.TrueForLoggedUsersFeature;
+import org.dspace.app.rest.authorization.TrueForTestUsersFeature;
+import org.dspace.app.rest.authorization.TrueForUsersInGroupTestFeature;
+import org.dspace.app.rest.builder.CommunityBuilder;
+import org.dspace.app.rest.builder.EPersonBuilder;
+import org.dspace.app.rest.builder.GroupBuilder;
+import org.dspace.app.rest.converter.ConverterService;
+import org.dspace.app.rest.matcher.AuthorizationMatcher;
+import org.dspace.app.rest.model.BaseObjectRest;
+import org.dspace.app.rest.model.CommunityRest;
+import org.dspace.app.rest.model.EPersonRest;
+import org.dspace.app.rest.model.ItemRest;
+import org.dspace.app.rest.model.SiteRest;
+import org.dspace.app.rest.projection.DefaultProjection;
+import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
+import org.dspace.app.rest.utils.Utils;
+import org.dspace.content.Community;
+import org.dspace.content.Site;
+import org.dspace.content.factory.ContentServiceFactory;
+import org.dspace.content.service.SiteService;
+import org.dspace.eperson.EPerson;
+import org.dspace.eperson.Group;
+import org.dspace.services.ConfigurationService;
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Test suite for the Authorization endpoint
+ *
+ * @author Andrea Bollini (andrea.bollini at 4science.it)
+ *
+ */
+public class AuthorizationRestRepositoryIT extends AbstractControllerIntegrationTest {
+
+ private static final Logger log = org.apache.logging.log4j.LogManager
+ .getLogger(AuthorizationRestRepositoryIT.class);
+
+ @Autowired
+ private AuthorizationFeatureService authorizationFeatureService;
+
+ @Autowired
+ private ConverterService converterService;
+
+ @Autowired
+ private ConfigurationService configurationService;
+
+ @Autowired
+ private Utils utils;
+
+ private SiteService siteService;
+
+ /**
+ * this hold a reference to the test feature {@link AlwaysTrueFeature}
+ */
+ private AuthorizationFeature alwaysTrue;
+
+ /**
+ * this hold a reference to the test feature {@link AlwaysFalseFeature}
+ */
+ private AuthorizationFeature alwaysFalse;
+
+ /**
+ * this hold a reference to the test feature {@link AlwaysThrowExceptionFeature}
+ */
+ private AuthorizationFeature alwaysException;
+
+ /**
+ * this hold a reference to the test feature {@link TrueForAdminsFeature}
+ */
+ private AuthorizationFeature trueForAdmins;
+
+ /**
+ * this hold a reference to the test feature {@link TrueForLoggedUsersFeature}
+ */
+ private AuthorizationFeature trueForLoggedUsers;
+
+ /**
+ * this hold a reference to the test feature {@link TrueForTestFeature}
+ */
+ private AuthorizationFeature trueForTestUsers;
+
+ /**
+ * this hold a reference to the test feature {@link TrueForUsersInGroupTestFeature}
+ */
+ private AuthorizationFeature trueForUsersInGroupTest;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ siteService = ContentServiceFactory.getInstance().getSiteService();
+ alwaysTrue = authorizationFeatureService.find(AlwaysTrueFeature.NAME);
+ alwaysFalse = authorizationFeatureService.find(AlwaysFalseFeature.NAME);
+ alwaysException = authorizationFeatureService.find(AlwaysThrowExceptionFeature.NAME);
+ trueForAdmins = authorizationFeatureService.find(TrueForAdminsFeature.NAME);
+ trueForLoggedUsers = authorizationFeatureService.find(TrueForLoggedUsersFeature.NAME);
+ trueForTestUsers = authorizationFeatureService.find(TrueForTestUsersFeature.NAME);
+ trueForUsersInGroupTest = authorizationFeatureService.find(TrueForUsersInGroupTestFeature.NAME);
+ }
+
+ @Test
+ /**
+ * This method is not implemented
+ *
+ * @throws Exception
+ */
+ public void findAllTest() throws Exception {
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken).perform(get("/api/authz/authorizations"))
+ .andExpect(status().isMethodNotAllowed());
+ getClient().perform(get("/api/authz/authorizations"))
+ .andExpect(status().isMethodNotAllowed());
+ }
+
+ @Test
+ /**
+ * Verify that an user can access a specific authorization
+ *
+ * @throws Exception
+ */
+ public void findOneTest() throws Exception {
+ Site site = siteService.findSite(context);
+ SiteRest siteRest = converterService.toRest(site, DefaultProjection.DEFAULT);
+
+ // define three authorizations that we know must exists
+ Authorization authAdminSite = new Authorization(admin, trueForAdmins, siteRest);
+ Authorization authNormalUserSite = new Authorization(eperson, trueForLoggedUsers, siteRest);
+ Authorization authAnonymousUserSite = new Authorization(null, alwaysTrue, siteRest);
+
+ // access the authorization for the admin user
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + authAdminSite.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",
+ Matchers.is(AuthorizationMatcher.matchAuthorization(authAdminSite))));
+
+ // access the authorization for a normal user
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ getClient(epersonToken).perform(get("/api/authz/authorizations/" + authNormalUserSite.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",
+ Matchers.is(AuthorizationMatcher.matchAuthorization(authNormalUserSite))));
+
+ // access the authorization for a normal user as administrator
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + authNormalUserSite.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",
+ Matchers.is(AuthorizationMatcher.matchAuthorization(authNormalUserSite))));
+
+ // access the authorization for an anonymous user
+ getClient().perform(get("/api/authz/authorizations/" + authAnonymousUserSite.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",
+ Matchers.is(AuthorizationMatcher.matchAuthorization(authAnonymousUserSite))));
+ }
+
+ @Test
+ /**
+ * Verify that the unauthorized return code is used in the appropriate scenarios
+ *
+ * @throws Exception
+ */
+ public void findOneUnauthorizedTest() throws Exception {
+ Site site = siteService.findSite(context);
+ SiteRest siteRest = converterService.toRest(site, DefaultProjection.DEFAULT);
+
+ // define two authorizations that we know must exists
+ Authorization authAdminSite = new Authorization(admin, alwaysTrue, siteRest);
+ Authorization authNormalUserSite = new Authorization(eperson, alwaysTrue, siteRest);
+
+ // try anonymous access to the authorization for the admin user
+ getClient().perform(get("/api/authz/authorizations/" + authAdminSite.getID()))
+ .andExpect(status().isUnauthorized());
+
+ // try anonymous access to the authorization for a normal user
+ getClient().perform(get("/api/authz/authorizations/" + authNormalUserSite.getID()))
+ .andExpect(status().isUnauthorized());
+ }
+
+ @Test
+ /**
+ * Verify that the forbidden return code is used in the appropriate scenarios
+ *
+ * @throws Exception
+ */
+ public void findOneForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Site site = siteService.findSite(context);
+ SiteRest siteRest = converterService.toRest(site, DefaultProjection.DEFAULT);
+ EPerson testEPerson = EPersonBuilder.createEPerson(context)
+ .withEmail("test-authorization@example.com")
+ .withPassword(password).build();
+ context.restoreAuthSystemState();
+
+ // define three authorizations that we know must exists
+ Authorization authAdminSite = new Authorization(admin, alwaysTrue, siteRest);
+ Authorization authNormalUserSite = new Authorization(eperson, alwaysTrue, siteRest);
+
+ String testToken = getAuthToken(testEPerson.getEmail(), password);
+
+ // try to access the authorization for the admin user with another user
+ getClient(testToken).perform(get("/api/authz/authorizations/" + authAdminSite.getID()))
+ .andExpect(status().isForbidden());
+
+ // try to access the authorization of a normal user with another user
+ getClient(testToken).perform(get("/api/authz/authorizations/" + authNormalUserSite.getID()))
+ .andExpect(status().isForbidden());
+
+ // check access as a test user to a not existing authorization for another
+ // eperson (but existing for the test user)
+ Authorization noTestAuthForNormalUserSite = new Authorization(eperson, trueForTestUsers, siteRest);
+ getClient(testToken).perform(get("/api/authz/authorizations/" + noTestAuthForNormalUserSite.getID()))
+ .andExpect(status().isForbidden());
+ }
+
+ @Test
+ /**
+ * Verify that the not found return code is used in the appropriate scenarios
+ *
+ * @throws Exception
+ */
+ public void findOneNotFoundTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Site site = siteService.findSite(context);
+ SiteRest siteRest = converterService.toRest(site, DefaultProjection.DEFAULT);
+ EPersonRest epersonRest = converterService.toRest(eperson, DefaultProjection.DEFAULT);
+ context.restoreAuthSystemState();
+
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ String adminToken = getAuthToken(admin.getEmail(), password);
+
+ // define three authorizations that we know will be no granted
+ Authorization authAdminSite = new Authorization(admin, alwaysFalse, siteRest);
+ Authorization authNormalUserSite = new Authorization(eperson, alwaysFalse, siteRest);
+ Authorization authAnonymousUserSite = new Authorization(null, alwaysFalse, siteRest);
+
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + authAdminSite.getID()))
+ .andExpect(status().isNotFound());
+
+ getClient(epersonToken).perform(get("/api/authz/authorizations/" + authNormalUserSite.getID()))
+ .andExpect(status().isNotFound());
+ // also the admin cannot retrieve a not existing authorization for the normal user
+ getClient(epersonToken).perform(get("/api/authz/authorizations/" + authNormalUserSite.getID()))
+ .andExpect(status().isNotFound());
+
+ getClient().perform(get("/api/authz/authorizations/" + authAnonymousUserSite.getID()))
+ .andExpect(status().isNotFound());
+ // also the admin cannot retrieve a not existing authorization for the anonymous user
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + authAnonymousUserSite.getID()))
+ .andExpect(status().isNotFound());
+
+ // build a couple of IDs that look good but are related to not existing authorizations
+ // the trueForAdmins feature is not defined for eperson
+ String authInvalidType = getAuthorizationID(admin, trueForAdmins, epersonRest);
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + authInvalidType))
+ .andExpect(status().isNotFound());
+
+ // the specified item doesn't exist
+ String authNotExistingObject = getAuthorizationID(admin, alwaysTrue,
+ ItemRest.CATEGORY + "." + ItemRest.NAME, UUID.randomUUID());
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + authNotExistingObject))
+ .andExpect(status().isNotFound());
+
+ // the specified eperson doesn't exist
+ String authNotExistingEPerson = getAuthorizationID(UUID.randomUUID(), alwaysTrue, siteRest);
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + authNotExistingEPerson))
+ .andExpect(status().isNotFound());
+
+ // the specified feature doesn't exist
+ String authNotExistingFeature = getAuthorizationID(admin, "notexistingfeature", siteRest);
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + authNotExistingFeature))
+ .andExpect(status().isNotFound());
+
+ // check access as admin to a not existing authorization for another eperson (but existing for the admin)
+ Authorization noAdminAuthForNormalUserSite = new Authorization(eperson, trueForAdmins, siteRest);
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + noAdminAuthForNormalUserSite.getID()))
+ .andExpect(status().isNotFound());
+
+ // check a couple of completely wrong IDs
+ String notValidID = "notvalidID";
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + notValidID))
+ .andExpect(status().isNotFound());
+
+ String notValidIDWithWrongEpersonPart = getAuthorizationID("1", alwaysTrue.getName(),
+ SiteRest.CATEGORY + "." + SiteRest.NAME, site.getID().toString());
+ // use the admin token otherwise it would result in a forbidden (attempt to access authorization of other users)
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + notValidIDWithWrongEpersonPart))
+ .andExpect(status().isNotFound());
+
+ String notValidIDWithWrongObjectTypePart = getAuthorizationID(eperson.getID().toString(), alwaysTrue.getName(),
+ "SITE", site.getID().toString());
+ getClient(epersonToken).perform(get("/api/authz/authorizations/" + notValidIDWithWrongObjectTypePart))
+ .andExpect(status().isNotFound());
+
+ String notValidIDWithUnknownObjectTypePart =
+ getAuthorizationID(eperson.getID().toString(), alwaysTrue.getName(),
+ "core.unknown", "1");
+ getClient(epersonToken).perform(get("/api/authz/authorizations/" + notValidIDWithUnknownObjectTypePart))
+ .andExpect(status().isNotFound());
+
+ }
+
+ @Test
+ /**
+ * Verify that an exception in the feature check will be reported back
+ *
+ * @throws Exception
+ */
+ public void findOneInternalServerErrorTest() throws Exception {
+ Site site = siteService.findSite(context);
+ SiteRest siteRest = converterService.toRest(site, DefaultProjection.DEFAULT);
+ // define two authorizations that we know will throw exceptions
+ Authorization authAdminSite = new Authorization(admin, alwaysException, siteRest);
+ Authorization authNormalUserSite = new Authorization(eperson, alwaysException, siteRest);
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + authAdminSite.getID()))
+ .andExpect(status().isInternalServerError());
+
+ getClient(epersonToken).perform(get("/api/authz/authorizations/" + authNormalUserSite.getID()))
+ .andExpect(status().isInternalServerError());
+ }
+
+ @Test
+ /**
+ * Verify that the search by object works properly in allowed scenarios:
+ * - for an administrator
+ * - for an administrator that want to inspect permission of the anonymous users or another user
+ * - for a logged-in "normal" user
+ * - for anonymous
+ *
+ * @throws Exception
+ */
+ public void findByObjectTest() throws Exception {
+ Site site = siteService.findSite(context);
+ SiteRest siteRest = converterService.toRest(site, converterService.getProjection(DefaultProjection.NAME));
+ String siteUri = utils.linkToSingleResource(siteRest, "self").getHref();
+
+ // disarm the alwaysThrowExceptionFeature
+ configurationService.setProperty("org.dspace.app.rest.authorization.AlwaysThrowExceptionFeature.turnoff", true);
+ // verify that it works for administrators
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/object")
+ .param("projection", "full")
+ .param("uri", siteUri)
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isOk())
+ // there are at least 3: alwaysTrue, trueForAdministrators and trueForLoggedUsers
+ .andExpect(jsonPath("$._embedded.authorizations", Matchers.hasSize(greaterThanOrEqualTo(3))))
+ .andExpect(jsonPath("$._embedded.authorizations", Matchers.everyItem(
+ Matchers.anyOf(
+ JsonPathMatchers.hasJsonPath("$.type", is("authorization")),
+ JsonPathMatchers.hasJsonPath("$._embedded.feature",
+ Matchers.allOf(
+ is(alwaysTrue.getName()),
+ is(trueForAdmins.getName()),
+ is(trueForLoggedUsers.getName())
+ )),
+ JsonPathMatchers.hasJsonPath("$._embedded.feature",
+ Matchers.not(Matchers.anyOf(
+ is(alwaysFalse.getName()),
+ is(alwaysException.getName()),
+ is(trueForTestUsers.getName())
+ )
+ )),
+ JsonPathMatchers.hasJsonPath("$._embedded.feature.resourcetypes",
+ Matchers.hasItem(is("authorization"))),
+ JsonPathMatchers.hasJsonPath("$.id",
+ Matchers.anyOf(
+ Matchers.startsWith(admin.getID().toString()),
+ Matchers.endsWith(siteRest.getUniqueType() + "_" + siteRest.getId()))))
+ )
+ )
+ )
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/authz/authorizations/search/object")))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", greaterThanOrEqualTo(3)));
+
+ // verify that it works for normal loggedin users
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ getClient(epersonToken).perform(get("/api/authz/authorizations/search/object")
+ .param("projection", "full")
+ .param("uri", siteUri)
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isOk())
+ // there are at least 2: alwaysTrue and trueForLoggedUsers
+ .andExpect(jsonPath("$._embedded.authorizations", Matchers.hasSize(greaterThanOrEqualTo(2))))
+ .andExpect(jsonPath("$._embedded.authorizations", Matchers.everyItem(
+ Matchers.anyOf(
+ JsonPathMatchers.hasJsonPath("$.type", is("authorization")),
+ JsonPathMatchers.hasJsonPath("$._embedded.feature",
+ Matchers.allOf(
+ is(alwaysTrue.getName()),
+ is(trueForLoggedUsers.getName())
+ )),
+ JsonPathMatchers.hasJsonPath("$._embedded.feature",
+ Matchers.not(Matchers.anyOf(
+ is(alwaysFalse.getName()),
+ is(alwaysException.getName()),
+ is(trueForTestUsers.getName()),
+ is(trueForAdmins.getName())
+ )
+ )),
+ JsonPathMatchers.hasJsonPath("$._embedded.feature.resourcetypes",
+ Matchers.hasItem(is("authorization"))),
+ JsonPathMatchers.hasJsonPath("$.id",
+ Matchers.anyOf(
+ Matchers.startsWith(eperson.getID().toString()),
+ Matchers.endsWith(siteRest.getUniqueType() + "_" + siteRest.getId()))))
+ )
+ )
+ )
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/authz/authorizations/search/object")))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", greaterThanOrEqualTo(2)));
+
+ // verify that it works for administators inspecting other users
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/object")
+ .param("projection", "full")
+ .param("uri", siteUri)
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isOk())
+ // there are at least 2: alwaysTrue and trueForLoggedUsers
+ .andExpect(jsonPath("$._embedded.authorizations", Matchers.hasSize(greaterThanOrEqualTo(2))))
+ .andExpect(jsonPath("$._embedded.authorizations", Matchers.everyItem(
+ Matchers.anyOf(
+ JsonPathMatchers.hasJsonPath("$.type", is("authorization")),
+ JsonPathMatchers.hasJsonPath("$._embedded.feature",
+ Matchers.allOf(
+ is(alwaysTrue.getName()),
+ is(trueForLoggedUsers.getName())
+ )),
+ JsonPathMatchers.hasJsonPath("$._embedded.feature",
+ Matchers.not(Matchers.anyOf(
+ is(alwaysFalse.getName()),
+ is(alwaysException.getName()),
+ is(trueForTestUsers.getName()),
+ // this guarantee that we are looking to the eperson
+ // authz and not to the admin ones
+ is(trueForAdmins.getName())
+ )
+ )),
+ JsonPathMatchers.hasJsonPath("$._embedded.feature.resourcetypes",
+ Matchers.hasItem(is("authorization"))),
+ JsonPathMatchers.hasJsonPath("$.id",
+ Matchers.anyOf(
+ // this guarantee that we are looking to the eperson
+ // authz and not to the admin ones
+ Matchers.startsWith(eperson.getID().toString()),
+ Matchers.endsWith(siteRest.getUniqueType() + "_" + siteRest.getId()))))
+ )
+ )
+ )
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/authz/authorizations/search/object")))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", greaterThanOrEqualTo(2)));
+
+ // verify that it works for anonymous users
+ getClient().perform(get("/api/authz/authorizations/search/object")
+ .param("projection", "full")
+ .param("uri", siteUri))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.authorizations", Matchers.hasSize(greaterThanOrEqualTo(1))))
+ .andExpect(jsonPath("$._embedded.authorizations", Matchers.everyItem(
+ Matchers.anyOf(
+ JsonPathMatchers.hasJsonPath("$.type", is("authorization")),
+ JsonPathMatchers.hasJsonPath("$._embedded.feature",
+ Matchers.allOf(
+ is(alwaysTrue.getName())
+ )),
+ JsonPathMatchers.hasJsonPath("$._embedded.feature",
+ Matchers.not(Matchers.anyOf(
+ is(alwaysFalse.getName()),
+ is(alwaysException.getName()),
+ is(trueForTestUsers.getName()),
+ is(trueForAdmins.getName())
+ )
+ )),
+ JsonPathMatchers.hasJsonPath("$._embedded.feature.resourcetypes",
+ Matchers.hasItem(is("authorization"))),
+ JsonPathMatchers.hasJsonPath("$.id",
+ Matchers.anyOf(
+ Matchers.startsWith(eperson.getID().toString()),
+ Matchers.endsWith(siteRest.getUniqueType() + "_" + siteRest.getId()))))
+ )
+ )
+ )
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/authz/authorizations/search/object")))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", greaterThanOrEqualTo(1)));
+
+ // verify that it works for administrators inspecting anonymous users
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/object")
+ .param("projection", "full")
+ .param("uri", siteUri))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.authorizations", Matchers.hasSize(greaterThanOrEqualTo(1))))
+ .andExpect(jsonPath("$._embedded.authorizations", Matchers.everyItem(
+ Matchers.anyOf(
+ JsonPathMatchers.hasJsonPath("$.type", is("authorization")),
+ JsonPathMatchers.hasJsonPath("$._embedded.feature",
+ Matchers.allOf(
+ is(alwaysTrue.getName())
+ )),
+ JsonPathMatchers.hasJsonPath("$._embedded.feature",
+ Matchers.not(Matchers.anyOf(
+ is(alwaysFalse.getName()),
+ is(alwaysException.getName()),
+ is(trueForTestUsers.getName()),
+ is(trueForAdmins.getName())
+ )
+ )),
+ JsonPathMatchers.hasJsonPath("$._embedded.feature.resourcetypes",
+ Matchers.hasItem(is("authorization"))),
+ JsonPathMatchers.hasJsonPath("$.id",
+ Matchers.anyOf(
+ Matchers.startsWith(eperson.getID().toString()),
+ Matchers.endsWith(siteRest.getUniqueType() + "_" + siteRest.getId()))))
+ )
+ )
+ )
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/authz/authorizations/search/object")))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", greaterThanOrEqualTo(1)));
+ }
+
+ @Test
+ /**
+ * Verify that the findByObject return an empty page when the requested object doesn't exist but the uri is
+ * potentially valid (i.e. deleted object)
+ *
+ * @throws Exception
+ */
+ public void findByNotExistingObjectTest() throws Exception {
+ String wrongSiteUri = "http://localhost/api/core/sites/" + UUID.randomUUID();
+
+ // disarm the alwaysThrowExceptionFeature
+ configurationService.setProperty("org.dspace.app.rest.authorization.AlwaysThrowExceptionFeature.turnoff", true);
+ // verify that it works for administrators, no result
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/object")
+ .param("uri", wrongSiteUri)
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$", JsonPathMatchers.hasNoJsonPath("$._embedded.authorizations")))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/authz/authorizations/search/object")))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", is(0)));
+
+ // verify that it works for normal loggedin users
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ getClient(epersonToken).perform(get("/api/authz/authorizations/search/object")
+ .param("uri", wrongSiteUri)
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$", JsonPathMatchers.hasNoJsonPath("$._embedded.authorizations")))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/authz/authorizations/search/object")))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", is(0)));
+
+ // verify that it works for administators inspecting other users
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/object")
+ .param("uri", wrongSiteUri)
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$", JsonPathMatchers.hasNoJsonPath("$._embedded.authorizations")))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/authz/authorizations/search/object")))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", is(0)));
+
+ // verify that it works for anonymous users
+ getClient().perform(get("/api/authz/authorizations/search/object")
+ .param("uri", wrongSiteUri))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$", JsonPathMatchers.hasNoJsonPath("$._embedded.authorizations")))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/authz/authorizations/search/object")))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", is(0)));
+
+ // verify that it works for administrators inspecting anonymous users
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/object")
+ .param("uri", wrongSiteUri))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$", JsonPathMatchers.hasNoJsonPath("$._embedded.authorizations")))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/authz/authorizations/search/object")))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", is(0)));
+ }
+
+ @Test
+ /**
+ * Verify that the findByObject return the 400 Bad Request response for invalid or missing URI (required parameter)
+ *
+ * @throws Exception
+ */
+ public void findByObjectBadRequestTest() throws Exception {
+ String[] invalidUris = new String[] {
+ "invalid-uri",
+ "",
+ "http://localhost/api/wrongcategory/wrongmodel/1",
+ "http://localhost/api/core/sites/this-is-not-an-uuid"
+ };
+
+ // disarm the alwaysThrowExceptionFeature
+ configurationService.setProperty("org.dspace.app.rest.authorization.AlwaysThrowExceptionFeature.turnoff", true);
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ for (String invalidUri : invalidUris) {
+ log.debug("findByObjectBadRequestTest - Testing the URI: " + invalidUri);
+ // verify that it works for administrators with an invalid or missing uri
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/object")
+ .param("uri", invalidUri)
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isBadRequest());
+
+ // verify that it works for normal loggedin users with an invalid or missing uri
+ getClient(epersonToken).perform(get("/api/authz/authorizations/search/object")
+ .param("uri", invalidUri)
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isBadRequest());
+
+ // verify that it works for administators inspecting other users with an invalid or missing uri
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/object")
+ .param("uri", invalidUri)
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isBadRequest());
+
+ // verify that it works for anonymous users with an invalid or missing uri
+ getClient().perform(get("/api/authz/authorizations/search/object")
+ .param("uri", invalidUri))
+ .andExpect(status().isBadRequest());
+
+ // verify that it works for administrators inspecting anonymous users with an invalid or missing uri
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/object")
+ .param("uri", invalidUri))
+ .andExpect(status().isBadRequest());
+ }
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/object")
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isBadRequest());
+ getClient(epersonToken).perform(get("/api/authz/authorizations/search/object")
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isBadRequest());
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/object")
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isBadRequest());
+ getClient().perform(get("/api/authz/authorizations/search/object"))
+ .andExpect(status().isBadRequest());
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/object"))
+ .andExpect(status().isBadRequest());
+ }
+
+ @Test
+ /**
+ * Verify that the findByObject return the 401 Unauthorized response when an eperson is involved
+ *
+ * @throws Exception
+ */
+ public void findByObjectUnauthorizedTest() throws Exception {
+ Site site = siteService.findSite(context);
+ SiteRest siteRest = converterService.toRest(site, converterService.getProjection(DefaultProjection.NAME));
+ String siteUri = utils.linkToSingleResource(siteRest, "self").getHref();
+
+ // disarm the alwaysThrowExceptionFeature
+ configurationService.setProperty("org.dspace.app.rest.authorization.AlwaysThrowExceptionFeature.turnoff", true);
+
+ getClient().perform(get("/api/authz/authorizations/search/object")
+ .param("uri", siteUri)
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isUnauthorized());
+
+ // verify that it works for normal loggedin users with an invalid or missing uri
+ getClient().perform(get("/api/authz/authorizations/search/object")
+ .param("uri", siteUri)
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isUnauthorized());
+ }
+
+ @Test
+ /**
+ * Verify that the findByObject return the 403 Forbidden response when a non-admin eperson try to search the
+ * authorization of another eperson
+ *
+ * @throws Exception
+ */
+ public void findByObjectForbiddenTest() throws Exception {
+ Site site = siteService.findSite(context);
+ SiteRest siteRest = converterService.toRest(site, converterService.getProjection(DefaultProjection.NAME));
+ String siteUri = utils.linkToSingleResource(siteRest, "self").getHref();
+ context.turnOffAuthorisationSystem();
+ EPerson anotherEperson = EPersonBuilder.createEPerson(context).withEmail("another@example.com")
+ .withPassword(password).build();
+ context.restoreAuthSystemState();
+ // disarm the alwaysThrowExceptionFeature
+ configurationService.setProperty("org.dspace.app.rest.authorization.AlwaysThrowExceptionFeature.turnoff", true);
+ String anotherToken = getAuthToken(anotherEperson.getEmail(), password);
+ // verify that he cannot search the admin authorizations
+ getClient(anotherToken).perform(get("/api/authz/authorizations/search/object")
+ .param("uri", siteUri)
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isForbidden());
+
+ // verify that he cannot search the authorizations of another "normal" eperson
+ getClient(anotherToken).perform(get("/api/authz/authorizations/search/object")
+ .param("uri", siteUri)
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isForbidden());
+ }
+
+ @Test
+ /**
+ * Verify that an exception in the feature check will be reported back
+ * @throws Exception
+ */
+ public void findByObjectInternalServerErrorTest() throws Exception {
+ Site site = siteService.findSite(context);
+ SiteRest siteRest = converterService.toRest(site, converterService.getProjection(DefaultProjection.NAME));
+ String siteUri = utils.linkToSingleResource(siteRest, "self").getHref();
+
+ // verify that it works for administrators
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/object")
+ .param("uri", siteUri)
+ // use a large page so that the alwaysThrowExceptionFeature is invoked
+ // this could become insufficient at some point
+ .param("size", "100")
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isInternalServerError());
+
+ // verify that it works for normal loggedin users
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ getClient(epersonToken).perform(get("/api/authz/authorizations/search/object")
+ .param("uri", siteUri)
+ // use a large page so that the alwaysThrowExceptionFeature is invoked
+ // this could become insufficient at some point
+ .param("size", "100")
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isInternalServerError());
+
+ // verify that it works for anonymous users
+ getClient().perform(get("/api/authz/authorizations/search/object")
+ .param("uri", siteUri)
+ // use a large page so that the alwaysThrowExceptionFeature is invoked
+ // this could become insufficient at some point
+ .param("size", "100"))
+ .andExpect(status().isInternalServerError());
+ }
+
+ @Test
+ /**
+ * Verify that the search by object and feature works properly in allowed scenarios:
+ * - for an administrator
+ * - for an administrator that want to inspect permission of the anonymous users or another user
+ * - for a logged-in "normal" user
+ * - for anonymous
+ *
+ * @throws Exception
+ */
+ public void findByObjectAndFeatureTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ Community com = CommunityBuilder.createCommunity(context).withName("A test community").build();
+ CommunityRest comRest = converterService.toRest(com, converterService.getProjection(DefaultProjection.NAME));
+ String comUri = utils.linkToSingleResource(comRest, "self").getHref();
+ context.restoreAuthSystemState();
+
+ // verify that it works for administrators
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", comUri)
+ .param("projection", "full")
+ .param("feature", alwaysTrue.getName())
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.type", is("authorization")))
+ .andExpect(jsonPath("$._embedded.feature.id", is(alwaysTrue.getName())))
+ .andExpect(jsonPath("$.id", Matchers.is(admin.getID().toString() + "_" + alwaysTrue.getName() + "_"
+ + comRest.getUniqueType() + "_" + comRest.getId())));
+
+ // verify that it works for normal loggedin users
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ getClient(epersonToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", comUri)
+ .param("projection", "full")
+ .param("feature", alwaysTrue.getName())
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.type", is("authorization")))
+ .andExpect(jsonPath("$._embedded.feature.id", is(alwaysTrue.getName())))
+ .andExpect(jsonPath("$.id", Matchers.is(eperson.getID().toString() + "_" + alwaysTrue.getName() + "_"
+ + comRest.getUniqueType() + "_" + comRest.getId())));
+
+ // verify that it works for administators inspecting other users
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", comUri)
+ .param("projection", "full")
+ .param("feature", alwaysTrue.getName())
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.type", is("authorization")))
+ .andExpect(jsonPath("$._embedded.feature.id", is(alwaysTrue.getName())))
+ .andExpect(jsonPath("$.id", Matchers.is(eperson.getID().toString() + "_" + alwaysTrue.getName() + "_"
+ + comRest.getUniqueType() + "_" + comRest.getId())));
+
+ // verify that it works for anonymous users
+ getClient().perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", comUri)
+ .param("projection", "full")
+ .param("feature", alwaysTrue.getName()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.type", is("authorization")))
+ .andExpect(jsonPath("$._embedded.feature.id", is(alwaysTrue.getName())))
+ .andExpect(jsonPath("$.id",Matchers.is(alwaysTrue.getName() + "_"
+ + comRest.getUniqueType() + "_" + comRest.getId())));
+
+ // verify that it works for administrators inspecting anonymous users
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", comUri)
+ .param("projection", "full")
+ .param("feature", alwaysTrue.getName()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.type", is("authorization")))
+ .andExpect(jsonPath("$._embedded.feature.id", is(alwaysTrue.getName())))
+ .andExpect(jsonPath("$.id",Matchers.is(alwaysTrue.getName() + "_"
+ + comRest.getUniqueType() + "_" + comRest.getId())));
+ }
+
+ @Test
+ /**
+ * Verify that the search by object and feature works return 204 No Content when a feature is not granted
+ *
+ * @throws Exception
+ */
+ public void findByObjectAndFeatureNotGrantedTest() throws Exception {
+ Site site = siteService.findSite(context);
+ SiteRest siteRest = converterService.toRest(site, converterService.getProjection(DefaultProjection.NAME));
+ String siteUri = utils.linkToSingleResource(siteRest, "self").getHref();
+
+ // verify that it works for administrators
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", alwaysFalse.getName())
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isNoContent());
+
+ // verify that it works for normal loggedin users
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ getClient(epersonToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", trueForAdmins.getName())
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isNoContent());
+
+ // verify that it works for administators inspecting other users
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", trueForAdmins.getName())
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isNoContent());
+
+ // verify that it works for anonymous users
+ getClient().perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", trueForLoggedUsers.getName()))
+ .andExpect(status().isNoContent());
+
+ // verify that it works for administrators inspecting anonymous users
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", trueForLoggedUsers.getName()))
+ .andExpect(status().isNoContent());
+ }
+
+ @Test
+ /**
+ * Verify that the findByObject return the 204 No Content code when the requested object doesn't exist but the uri
+ * is potentially valid (i.e. deleted object) or the feature doesn't exist
+ *
+ * @throws Exception
+ */
+ public void findByNotExistingObjectAndFeatureTest() throws Exception {
+ String wrongSiteUri = "http://localhost/api/core/sites/" + UUID.randomUUID();
+ Site site = siteService.findSite(context);
+ SiteRest siteRest = converterService.toRest(site, converterService.getProjection(DefaultProjection.NAME));
+ String siteUri = utils.linkToSingleResource(siteRest, "self").getHref();
+
+ // disarm the alwaysThrowExceptionFeature
+ configurationService.setProperty("org.dspace.app.rest.authorization.AlwaysThrowExceptionFeature.turnoff", true);
+ // verify that it works for administrators, no result
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", wrongSiteUri)
+ .param("feature", alwaysTrue.getName())
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isNoContent());
+
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", "not-existing-feature")
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isNoContent());
+
+ // verify that it works for normal loggedin users
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ getClient(epersonToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", wrongSiteUri)
+ .param("feature", alwaysTrue.getName())
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isNoContent());
+
+ getClient(epersonToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", "not-existing-feature")
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isNoContent());
+
+ // verify that it works for administators inspecting other users
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", wrongSiteUri)
+ .param("feature", alwaysTrue.getName())
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isNoContent());
+
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", "not-existing-feature")
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isNoContent());
+
+ // verify that it works for anonymous users
+ getClient().perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", wrongSiteUri)
+ .param("feature", alwaysTrue.getName()))
+ .andExpect(status().isNoContent());
+
+ getClient().perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", "not-existing-feature"))
+ .andExpect(status().isNoContent());
+
+ // verify that it works for administrators inspecting anonymous users
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", wrongSiteUri)
+ .param("feature", alwaysTrue.getName()))
+ .andExpect(status().isNoContent());
+
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", "not-existing-feature"))
+ .andExpect(status().isNoContent());
+ }
+
+ @Test
+ /**
+ * Verify that the findByObject return the 400 Bad Request response for invalid or missing URI or feature (required
+ * parameters)
+ *
+ * @throws Exception
+ */
+ public void findByObjectAndFeatureBadRequestTest() throws Exception {
+ String[] invalidUris = new String[] {
+ "invalid-uri",
+ "",
+ "http://localhost/api/wrongcategory/wrongmodel/1",
+ "http://localhost/api/core/sites/this-is-not-an-uuid"
+ };
+ Site site = siteService.findSite(context);
+ SiteRest siteRest = converterService.toRest(site, converterService.getProjection(DefaultProjection.NAME));
+ String siteUri = utils.linkToSingleResource(siteRest, "self").getHref();
+ // disarm the alwaysThrowExceptionFeature
+ configurationService.setProperty("org.dspace.app.rest.authorization.AlwaysThrowExceptionFeature.turnoff", true);
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ for (String invalidUri : invalidUris) {
+ log.debug("findByObjectAndFeatureBadRequestTest - Testing the URI: " + invalidUri);
+ // verify that it works for administrators with an invalid or missing uri
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", invalidUri)
+ .param("feature", alwaysTrue.getName())
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isBadRequest());
+
+ // verify that it works for normal loggedin users with an invalid or missing uri
+ getClient(epersonToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", invalidUri)
+ .param("feature", alwaysTrue.getName())
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isBadRequest());
+
+ // verify that it works for administators inspecting other users with an invalid or missing uri
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", invalidUri)
+ .param("feature", alwaysTrue.getName())
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isBadRequest());
+
+ // verify that it works for anonymous users with an invalid or missing uri
+ getClient().perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", invalidUri)
+ .param("feature", alwaysTrue.getName()))
+ .andExpect(status().isBadRequest());
+
+ // verify that it works for administrators inspecting anonymous users with an invalid or missing uri
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", invalidUri)
+ .param("feature", alwaysTrue.getName()))
+ .andExpect(status().isBadRequest());
+ }
+
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isBadRequest());
+ getClient(epersonToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isBadRequest());
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isBadRequest());
+ getClient().perform(get("/api/authz/authorizations/search/objectAndFeature"))
+ .andExpect(status().isBadRequest());
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature"))
+ .andExpect(status().isBadRequest());
+
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isBadRequest());
+ getClient(epersonToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isBadRequest());
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isBadRequest());
+ getClient().perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri))
+ .andExpect(status().isBadRequest());
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri.toString()))
+ .andExpect(status().isBadRequest());
+ }
+
+ @Test
+ /**
+ * Verify that the findByObjectAndFeature return the 401 Unauthorized response when an eperson is involved
+ *
+ * @throws Exception
+ */
+ public void findByObjectAndFeatureUnauthorizedTest() throws Exception {
+ Site site = siteService.findSite(context);
+ SiteRest siteRest = converterService.toRest(site, converterService.getProjection(DefaultProjection.NAME));
+ String siteUri = utils.linkToSingleResource(siteRest, "self").getHref();
+
+ // disarm the alwaysThrowExceptionFeature
+ configurationService.setProperty("org.dspace.app.rest.authorization.AlwaysThrowExceptionFeature.turnoff", true);
+
+ getClient().perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", alwaysTrue.getName())
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isUnauthorized());
+
+ // verify that it works for normal loggedin users with an invalid or missing uri
+ getClient().perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", alwaysTrue.getName())
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isUnauthorized());
+ }
+
+ @Test
+ /**
+ * Verify that the findByObjectAndFeature return the 403 Forbidden response when a non-admin eperson try to search
+ * the authorization of another eperson
+ *
+ * @throws Exception
+ */
+ public void findByObjectAndFeatureForbiddenTest() throws Exception {
+ Site site = siteService.findSite(context);
+ SiteRest siteRest = converterService.toRest(site, converterService.getProjection(DefaultProjection.NAME));
+ String siteUri = utils.linkToSingleResource(siteRest, "self").getHref();
+ context.turnOffAuthorisationSystem();
+ EPerson anotherEperson = EPersonBuilder.createEPerson(context).withEmail("another@example.com")
+ .withPassword(password).build();
+ context.restoreAuthSystemState();
+ // disarm the alwaysThrowExceptionFeature
+ configurationService.setProperty("org.dspace.app.rest.authorization.AlwaysThrowExceptionFeature.turnoff", true);
+ String anotherToken = getAuthToken(anotherEperson.getEmail(), password);
+ // verify that he cannot search the admin authorizations
+ getClient(anotherToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", alwaysTrue.getName())
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isForbidden());
+
+ // verify that he cannot search the authorizations of another "normal" eperson
+ getClient(anotherToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", alwaysTrue.getName())
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isForbidden());
+ }
+
+ @Test
+ /**
+ * Verify that an exception in the feature check will be reported back
+ * @throws Exception
+ */
+ public void findByObjectAndFeatureInternalServerErrorTest() throws Exception {
+ Site site = siteService.findSite(context);
+ SiteRest siteRest = converterService.toRest(site, converterService.getProjection(DefaultProjection.NAME));
+ String siteUri = utils.linkToSingleResource(siteRest, "self").getHref();
+
+ // verify that it works for administrators
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", alwaysException.getName())
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isInternalServerError());
+
+ // verify that it works for normal loggedin users
+ String epersonToken = getAuthToken(eperson.getEmail(), password);
+ getClient(epersonToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", alwaysException.getName())
+ .param("eperson", eperson.getID().toString()))
+ .andExpect(status().isInternalServerError());
+
+ // verify that it works for anonymous users
+ getClient().perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", alwaysException.getName()))
+ .andExpect(status().isInternalServerError());
+ }
+
+ @Test
+ /**
+ * This test will check that special group are correctly used to verify
+ * authorization for the current loggedin user but not inherited from the
+ * Administrators login when they look to authorization of third users
+ *
+ * @throws Exception
+ */
+ public void verifySpecialGroupMembershipTest() throws Exception {
+ Site site = siteService.findSite(context);
+ SiteRest siteRest = converterService.toRest(site, converterService.getProjection(DefaultProjection.NAME));
+ String siteUri = utils.linkToSingleResource(siteRest, "self").getHref();
+ context.turnOffAuthorisationSystem();
+ // create two normal users and put one in the test group directly
+ EPerson memberOfTestGroup = EPersonBuilder.createEPerson(context).withEmail("memberGroupTest@example.com")
+ .withPassword(password).build();
+ EPerson normalUser = EPersonBuilder.createEPerson(context).withEmail("normal@example.com")
+ .withPassword(password).build();
+ Group testGroup = GroupBuilder.createGroup(context).withName(TrueForUsersInGroupTestFeature.GROUP_NAME)
+ .addMember(memberOfTestGroup).build();
+ context.restoreAuthSystemState();
+
+ Authorization authAdminSite = new Authorization(admin, trueForUsersInGroupTest, siteRest);
+ Authorization authMemberSite = new Authorization(memberOfTestGroup, trueForUsersInGroupTest, siteRest);
+ Authorization authNormalUserSite = new Authorization(normalUser, trueForUsersInGroupTest, siteRest);
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+ String normalUserToken = getAuthToken(normalUser.getEmail(), password);
+ String memberToken = getAuthToken(memberOfTestGroup.getEmail(), password);
+
+ // proof that our admin doesn't have the special trueForUsersInGroupTest feature
+ // check both via direct access than via a search method
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + authAdminSite.getID()))
+ .andExpect(status().isNotFound());
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", trueForUsersInGroupTest.getName())
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isNoContent());
+ // nor the normal user both directly than if checked by the admin
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + authNormalUserSite.getID()))
+ .andExpect(status().isNotFound());
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", trueForUsersInGroupTest.getName())
+ .param("eperson", normalUser.getID().toString()))
+ .andExpect(status().isNoContent());
+ getClient(normalUserToken).perform(get("/api/authz/authorizations/" + authNormalUserSite.getID()))
+ .andExpect(status().isNotFound());
+ getClient(normalUserToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", trueForUsersInGroupTest.getName())
+ .param("eperson", normalUser.getID().toString()))
+ .andExpect(status().isNoContent());
+
+ // instead the member user has
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + authMemberSite.getID()))
+ .andExpect(status().isOk());
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", trueForUsersInGroupTest.getName())
+ .param("eperson", memberOfTestGroup.getID().toString()))
+ .andExpect(status().isOk());
+ // so it can also check itself the permission
+ getClient(memberToken).perform(get("/api/authz/authorizations/" + authMemberSite.getID()))
+ .andExpect(status().isOk());
+ getClient(memberToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", trueForUsersInGroupTest.getName())
+ .param("eperson", memberOfTestGroup.getID().toString()))
+ .andExpect(status().isOk());
+
+ // now configure the password login to grant special membership to our test group and login again our users
+ configurationService.setProperty("authentication-password.login.specialgroup",
+ TrueForUsersInGroupTestFeature.GROUP_NAME);
+ adminToken = getAuthToken(admin.getEmail(), password);
+ normalUserToken = getAuthToken(normalUser.getEmail(), password);
+ memberToken = getAuthToken(memberOfTestGroup.getEmail(), password);
+
+ // our admin now should have the authorization
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + authAdminSite.getID()))
+ .andExpect(status().isOk());
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", trueForUsersInGroupTest.getName())
+ .param("eperson", admin.getID().toString()))
+ .andExpect(status().isOk());
+ // our normal user when checked via the admin should still not have the authorization
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + authNormalUserSite.getID()))
+ .andExpect(status().isNotFound());
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", trueForUsersInGroupTest.getName())
+ .param("eperson", normalUser.getID().toString()))
+ .andExpect(status().isNoContent());
+ // but he should have the authorization if loggedin directly
+ getClient(normalUserToken).perform(get("/api/authz/authorizations/" + authNormalUserSite.getID()))
+ .andExpect(status().isOk());
+ getClient(normalUserToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", trueForUsersInGroupTest.getName())
+ .param("eperson", normalUser.getID().toString()))
+ .andExpect(status().isOk());
+ // for our direct member user we don't expect differences
+ getClient(adminToken).perform(get("/api/authz/authorizations/" + authMemberSite.getID()))
+ .andExpect(status().isOk());
+ getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", trueForUsersInGroupTest.getName())
+ .param("eperson", memberOfTestGroup.getID().toString()))
+ .andExpect(status().isOk());
+ getClient(memberToken).perform(get("/api/authz/authorizations/" + authMemberSite.getID()))
+ .andExpect(status().isOk());
+ getClient(memberToken).perform(get("/api/authz/authorizations/search/objectAndFeature")
+ .param("uri", siteUri)
+ .param("feature", trueForUsersInGroupTest.getName())
+ .param("eperson", memberOfTestGroup.getID().toString()))
+ .andExpect(status().isOk());
+ }
+
+ // utility methods to build authorization ID without having an authorization object
+ private String getAuthorizationID(EPerson eperson, AuthorizationFeature feature, BaseObjectRest obj) {
+ return getAuthorizationID(eperson != null ? eperson.getID().toString() : null, feature.getName(),
+ obj.getUniqueType(), obj.getId());
+ }
+
+ private String getAuthorizationID(UUID epersonUuid, AuthorizationFeature feature, BaseObjectRest obj) {
+ return getAuthorizationID(epersonUuid != null ? epersonUuid.toString() : null, feature.getName(),
+ obj.getUniqueType(), obj.getId());
+ }
+
+ private String getAuthorizationID(EPerson eperson, String featureName, BaseObjectRest obj) {
+ return getAuthorizationID(eperson != null ? eperson.getID().toString() : null, featureName,
+ obj.getUniqueType(), obj.getId());
+ }
+
+ private String getAuthorizationID(EPerson eperson, AuthorizationFeature feature, String objUniqueType,
+ Serializable objID) {
+ return getAuthorizationID(eperson != null ? eperson.getID().toString() : null, feature.getName(),
+ objUniqueType, objID);
+ }
+
+ private String getAuthorizationID(String epersonUuid, String featureName, String type, Serializable id) {
+ return (epersonUuid != null ? epersonUuid + "_" : "") + featureName + "_" + type + "_"
+ + id.toString();
+ }
+
+}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamControllerIT.java
index b42ca0e5be..608232ef5d 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamControllerIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamControllerIT.java
@@ -351,6 +351,61 @@ public class BitstreamControllerIT extends AbstractControllerIntegrationTest {
}
+ @Test
+ public void putOnBitstreamInOneBundleForbiddenTest() throws Exception {
+
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
+
+ Item publicItem1 = ItemBuilder.createItem(context, col1)
+ .withTitle("Test")
+ .withIssueDate("2016-11-11")
+ .withAuthor("Smith, Donald")
+ .withSubject("ExtraEntry")
+ .build();
+
+ Item targetItem = ItemBuilder.createItem(context, col1)
+ .withTitle("Test")
+ .withIssueDate("2016-11-11")
+ .withAuthor("Smith, Donald")
+ .withSubject("ExtraEntry")
+ .build();
+
+
+ Bundle bundle1 = BundleBuilder.createBundle(context, publicItem1)
+ .withName("TEST FIRST BUNDLE")
+ .build();
+
+ Bundle targetBundle = BundleBuilder.createBundle(context, targetItem)
+ .withName("TARGET BUNDLE")
+ .build();
+
+ String bitstreamContent = "ThisIsSomeDummyText";
+ Bitstream bitstream = null;
+ try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
+ bitstream = BitstreamBuilder.createBitstream(context, bundle1, is)
+ .withName("Bitstream")
+ .withDescription("description")
+ .withMimeType("text/plain")
+ .build();
+ }
+
+ context.restoreAuthSystemState();
+ String token = getAuthToken(eperson.getEmail(), password);
+
+ getClient(token).perform(put("/api/core/bitstreams/" + bitstream.getID() + "/bundle")
+ .contentType(parseMediaType(TEXT_URI_LIST_VALUE))
+ .content("https://localhost:8080/spring-rest/api/core/bundles/" + targetBundle.getID()))
+ .andExpect(status().isForbidden());
+ }
+
@Test
public void putOnBitstreamInMultipleBundles() throws Exception {
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java
index 2578ae01e2..6912e48ef3 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java
@@ -8,7 +8,6 @@
package org.dspace.app.rest;
import static java.util.UUID.randomUUID;
-
import static org.apache.commons.codec.CharEncoding.UTF_8;
import static org.apache.commons.collections.CollectionUtils.isEmpty;
import static org.apache.commons.io.IOUtils.toInputStream;
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BundleRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BundleRestRepositoryIT.java
index 465b15eac6..c4c59cca55 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BundleRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BundleRestRepositoryIT.java
@@ -45,6 +45,7 @@ import org.dspace.app.rest.model.patch.MoveOperation;
import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.authorize.ResourcePolicy;
+import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.Collection;
@@ -54,10 +55,14 @@ import org.dspace.eperson.EPerson;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.MvcResult;
public class BundleRestRepositoryIT extends AbstractControllerIntegrationTest {
+ @Autowired
+ ResourcePolicyService resourcePolicyService;
+
private Collection collection;
private Item item;
private Bundle bundle1;
@@ -136,6 +141,30 @@ public class BundleRestRepositoryIT extends AbstractControllerIntegrationTest {
;
}
+ @Test
+ public void findOneForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ String bitstreamContent = "Dummy content";
+ try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
+ bitstream1 = BitstreamBuilder.createBitstream(context, item, is)
+ .withName("Bitstream")
+ .withMimeType("text/plain")
+ .build();
+ }
+
+ bundle1 = BundleBuilder.createBundle(context, item)
+ .withName("testname")
+ .withBitstream(bitstream1)
+ .build();
+
+ resourcePolicyService.removePolicies(context, bundle1, Constants.READ);
+ context.restoreAuthSystemState();
+
+ String tokenEperson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEperson).perform(get("/api/core/bundles/" + bundle1.getID()))
+ .andExpect(status().isForbidden());
+ }
@Test
public void getItemBundles() throws Exception {
@@ -385,6 +414,38 @@ public class BundleRestRepositoryIT extends AbstractControllerIntegrationTest {
)));
}
+ @Test
+ public void getBitstreamsForBundleForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ String bitstreamContent = "Dummy content";
+ try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
+ bitstream1 = BitstreamBuilder.createBitstream(context, item, is)
+ .withName("Bitstream")
+ .withDescription("Description")
+ .withMimeType("text/plain")
+ .build();
+ bitstream2 = BitstreamBuilder.createBitstream(context, item, is)
+ .withName("Bitstream2")
+ .withDescription("Description2")
+ .withMimeType("text/plain")
+ .build();
+ }
+
+ bundle1 = BundleBuilder.createBundle(context, item)
+ .withName("testname")
+ .withBitstream(bitstream1)
+ .withBitstream(bitstream2)
+ .build();
+
+ resourcePolicyService.removePolicies(context, bundle1, Constants.READ);
+ context.restoreAuthSystemState();
+
+ String tokenEperson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEperson).perform(get("/api/core/bundles/" + bundle1.getID() + "/bitstreams"))
+ .andExpect(status().isForbidden());
+ }
+
@Test
public void patchMoveBitstreams() throws Exception {
context.turnOffAuthorisationSystem();
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java
index 547441e73f..80f87d06ed 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java
@@ -24,6 +24,7 @@ import java.util.UUID;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
+import org.dspace.app.rest.builder.EPersonBuilder;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.matcher.CollectionMatcher;
import org.dspace.app.rest.matcher.CommunityMatcher;
@@ -37,6 +38,7 @@ import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.app.rest.test.MetadataPatchSuite;
import org.dspace.authorize.service.AuthorizeService;
+import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.core.Constants;
@@ -54,6 +56,9 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
@Autowired
AuthorizeService authorizeService;
+ @Autowired
+ ResourcePolicyService resoucePolicyService;
+
@Test
public void findAllTest() throws Exception {
@@ -86,6 +91,108 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
)));
}
+ @Test
+ public void findAllUnAuthenticatedTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+
+ Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Collection 1")
+ .build();
+ Collection col2 = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Collection 2")
+ .build();
+
+ resoucePolicyService.removePolicies(context, col2, Constants.READ);
+ context.restoreAuthSystemState();
+
+ // anonymous can see only public collections
+ getClient().perform(get("/api/core/collections"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.collections", Matchers.contains(
+ CollectionMatcher.matchCollection(col1))))
+ .andExpect(jsonPath("$.page.totalElements", is(1)));
+ }
+
+ @Test
+ public void findAllForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+
+ Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Collection 1")
+ .build();
+ Collection col2 = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Collection 2")
+ .build();
+
+ resoucePolicyService.removePolicies(context, col2, Constants.READ);
+ context.restoreAuthSystemState();
+
+ // eperson logged can see only public collections
+ String tokenEperson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEperson).perform(get("/api/core/collections"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.collections", Matchers.contains(
+ CollectionMatcher.matchCollection(col1))))
+ .andExpect(jsonPath("$.page.totalElements", is(1)));
+ }
+
+ @Test
+ public void findAllGrantAccessAdminsTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ EPerson parentAdmin = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson1@mail.com")
+ .withPassword("qwerty01")
+ .build();
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .withAdminGroup(parentAdmin)
+ .build();
+
+ EPerson col1Admin = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson2@mail.com")
+ .withPassword("qwerty02")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Collection 1")
+ .withAdminGroup(col1Admin)
+ .build();
+
+ Collection col2 = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Collection 2")
+ .build();
+
+ resoucePolicyService.removePolicies(context, parentCommunity, Constants.READ);
+ resoucePolicyService.removePolicies(context, col1, Constants.READ);
+ context.restoreAuthSystemState();
+
+ // parent community admin can see all sub collections
+ String tokenParentAdmin = getAuthToken(parentAdmin.getEmail(), "qwerty01");
+ getClient(tokenParentAdmin).perform(get("/api/core/collections"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.collections", Matchers.containsInAnyOrder(
+ CollectionMatcher.matchCollection(col1),
+ CollectionMatcher.matchCollection(col2))))
+ .andExpect(jsonPath("$.page.totalElements", is(2)));
+
+ // admin of col1 can see owner collections and any public collections
+ String tokenCol1Admin = getAuthToken(col1Admin.getEmail(), "qwerty02");
+ getClient(tokenCol1Admin).perform(get("/api/core/collections"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.collections", Matchers.containsInAnyOrder(
+ CollectionMatcher.matchCollection(col1),
+ CollectionMatcher.matchCollection(col2))))
+ .andExpect(jsonPath("$.page.totalElements", is(2)));
+ }
+
@Test
public void findAllPaginationTest() throws Exception {
@@ -182,6 +289,100 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
col1.getName(), col1.getID(), col1.getHandle())));
}
+ @Test
+ public void findOneCollectionUnAuthenticatedTest() throws Exception {
+
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+
+ Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Collection 1")
+ .build();
+
+ resoucePolicyService.removePolicies(context, col1, Constants.READ);
+ context.restoreAuthSystemState();
+
+ getClient().perform(get("/api/core/collections/" + col1.getID()))
+ .andExpect(status().isUnauthorized());
+ }
+
+ @Test
+ public void findOneCollectionForbiddenTest() throws Exception {
+
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+
+ Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Collection 1")
+ .build();
+
+ resoucePolicyService.removePolicies(context, col1, Constants.READ);
+ context.restoreAuthSystemState();
+
+ String tokenEperson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEperson).perform(get("/api/core/collections/" + col1.getID()))
+ .andExpect(status().isForbidden());
+ }
+
+ @Test
+ public void findOneCollectionGrantAccessAdminsTest() throws Exception {
+
+ context.turnOffAuthorisationSystem();
+
+ EPerson parentAdmin = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson1@mail.com")
+ .withPassword("qwerty01")
+ .build();
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .withAdminGroup(parentAdmin)
+ .build();
+
+ EPerson col1Admin = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson2@mail.com")
+ .withPassword("qwerty02")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Collection 1")
+ .withAdminGroup(col1Admin)
+ .build();
+
+ EPerson col2Admin = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson3@mail.com")
+ .withPassword("qwerty03")
+ .build();
+ Collection col2 = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Collection 2")
+ .withAdminGroup(col2Admin)
+ .build();
+
+ resoucePolicyService.removePolicies(context, parentCommunity, Constants.READ);
+ resoucePolicyService.removePolicies(context, col1, Constants.READ);
+ resoucePolicyService.removePolicies(context, col2, Constants.READ);
+ context.restoreAuthSystemState();
+
+ String tokenParentAdmin = getAuthToken(parentAdmin.getEmail(), "qwerty01");
+ getClient(tokenParentAdmin).perform(get("/api/core/collections/" + col1.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",
+ Matchers.is((CollectionMatcher.matchCollection(col1)))));
+
+ String tokenCol1Admin = getAuthToken(col1Admin.getEmail(), "qwerty02");
+ getClient(tokenCol1Admin).perform(get("/api/core/collections/" + col1.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",
+ Matchers.is((CollectionMatcher.matchCollection(col1)))));
+
+ String tokenCol2Admin = getAuthToken(col2Admin.getEmail(), "qwerty03");
+ getClient(tokenCol2Admin).perform(get("/api/core/collections/" + col1.getID()))
+ .andExpect(status().isForbidden());
+ }
@Test
public void findOneCollectionRelsTest() throws Exception {
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityRestRepositoryIT.java
index 0a315aa5c3..0107e0f247 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityRestRepositoryIT.java
@@ -32,7 +32,9 @@ import java.util.stream.StreamSupport;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
+import org.dspace.app.rest.builder.EPersonBuilder;
import org.dspace.app.rest.converter.ConverterService;
+import org.dspace.app.rest.matcher.CollectionMatcher;
import org.dspace.app.rest.matcher.CommunityMatcher;
import org.dspace.app.rest.matcher.HalMatcher;
import org.dspace.app.rest.matcher.MetadataMatcher;
@@ -44,6 +46,7 @@ import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.app.rest.test.MetadataPatchSuite;
import org.dspace.authorize.service.AuthorizeService;
+import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.service.CommunityService;
@@ -71,6 +74,9 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
@Autowired
AuthorizeService authorizeService;
+ @Autowired
+ ResourcePolicyService resoucePolicyService;
+
@Test
public void createTest() throws Exception {
ObjectMapper mapper = new ObjectMapper();
@@ -528,6 +534,110 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
;
}
+ @Test
+ public void findAllUnAuthenticatedTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+ Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community 2")
+ .build();
+
+ resoucePolicyService.removePolicies(context, parentCommunity, Constants.READ);
+ resoucePolicyService.removePolicies(context, child1, Constants.READ);
+ context.restoreAuthSystemState();
+
+ // anonymous can see only public communities
+ getClient().perform(get("/api/core/communities"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.communities", Matchers.contains(
+ CommunityMatcher.matchCommunity(child2))))
+ .andExpect(jsonPath("$.page.totalElements", is(1)));
+ }
+
+ @Test
+ public void findAllForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+ Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community 2")
+ .build();
+
+ resoucePolicyService.removePolicies(context, parentCommunity, Constants.READ);
+ resoucePolicyService.removePolicies(context, child1, Constants.READ);
+ context.restoreAuthSystemState();
+
+ String tokenEperson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEperson).perform(get("/api/core/communities"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.communities", Matchers.contains(
+ CommunityMatcher.matchCommunity(child2))))
+ .andExpect(jsonPath("$.page.totalElements", is(1)));
+ }
+
+ @Test
+ public void findAllGrantAccessAdminsTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ EPerson parentAdmin = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson1@mail.com")
+ .withPassword("qwerty01")
+ .build();
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .withAdminGroup(parentAdmin)
+ .build();
+
+ EPerson child1Admin = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson2@mail.com")
+ .withPassword("qwerty02")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community 1")
+ .withAdminGroup(child1Admin)
+ .build();
+
+ Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community 2")
+ .build();
+
+ resoucePolicyService.removePolicies(context, parentCommunity, Constants.READ);
+ resoucePolicyService.removePolicies(context, child1, Constants.READ);
+ context.restoreAuthSystemState();
+
+ String tokenParentAdmin = getAuthToken(parentAdmin.getEmail(), "qwerty01");
+ getClient(tokenParentAdmin).perform(get("/api/core/communities"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.communities", Matchers.containsInAnyOrder(
+ CommunityMatcher.matchCommunity(parentCommunity),
+ CommunityMatcher.matchCommunity(child1),
+ CommunityMatcher.matchCommunity(child2))))
+ .andExpect(jsonPath("$.page.totalElements", is(3)));
+
+ String tokenChild1Admin = getAuthToken(child1Admin.getEmail(), "qwerty02");
+ getClient(tokenChild1Admin).perform(get("/api/core/communities"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.communities", Matchers.containsInAnyOrder(
+ CommunityMatcher.matchCommunity(child1),
+ CommunityMatcher.matchCommunity(child2))))
+ .andExpect(jsonPath("$.page.totalElements", is(2)));
+ }
+
@Test
public void findOneTest() throws Exception {
//We turn off the authorization system in order to create the structure as defined below
@@ -564,6 +674,86 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
parentCommunity.getName(), parentCommunity.getID(), parentCommunity.getHandle())));
}
+ @Test
+ public void findOneUnAuthenticatedTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ Community privateCommunity = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Private Community")
+ .build();
+
+ resoucePolicyService.removePolicies(context, privateCommunity, Constants.READ);
+
+ context.restoreAuthSystemState();
+
+ getClient().perform(get("/api/core/communities/" + privateCommunity.getID().toString()))
+ .andExpect(status().isUnauthorized());
+ }
+
+ @Test
+ public void findOneForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ Community privateCommunity = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Private Community")
+ .build();
+
+ resoucePolicyService.removePolicies(context, privateCommunity, Constants.READ);
+
+ context.restoreAuthSystemState();
+
+ String tokenEperson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEperson).perform(get("/api/core/communities/" + privateCommunity.getID().toString()))
+ .andExpect(status().isForbidden());
+ }
+
+ @Test
+ public void findOneGrantAccessAdminsTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .withAdminGroup(eperson)
+ .build();
+
+ EPerson privateCommunityAdmin = EPersonBuilder.createEPerson(context)
+ .withEmail("comunityAdmin@mail.com")
+ .withPassword("qwerty01")
+ .build();
+ Community privateCommunity = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .withAdminGroup(privateCommunityAdmin)
+ .build();
+
+ EPerson privateCommunityAdmin2 = EPersonBuilder.createEPerson(context)
+ .withEmail("comunityAdmin2@mail.com")
+ .withPassword("qwerty02")
+ .build();
+ Community privateCommunity2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community 2")
+ .withAdminGroup(privateCommunityAdmin2)
+ .build();
+
+ resoucePolicyService.removePolicies(context, privateCommunity, Constants.READ);
+
+ context.restoreAuthSystemState();
+
+ String tokenParentComunityAdmin = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenParentComunityAdmin).perform(get("/api/core/communities/" + privateCommunity.getID().toString()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$", Matchers.is(CommunityMatcher.matchCommunity(privateCommunity))));
+
+ String tokenCommunityAdmin = getAuthToken(privateCommunityAdmin.getEmail(), "qwerty01");
+ getClient(tokenCommunityAdmin).perform(get("/api/core/communities/" + privateCommunity.getID().toString()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$", Matchers.is(CommunityMatcher.matchCommunity(privateCommunity))));
+
+ String tokenComunityAdmin2 = getAuthToken(privateCommunityAdmin2.getEmail(), "qwerty02");
+ getClient(tokenComunityAdmin2).perform(get("/api/core/communities/"
+ + privateCommunity.getID().toString()))
+ .andExpect(status().isForbidden());
+ }
+
@Test
public void findOneRelsTest() throws Exception {
//We turn off the authorization system in order to create the structure as defined below
@@ -615,7 +805,7 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
getClient().perform(get("/api/core/communities/" + child1.getID().toString() + "/logo"))
.andExpect(status().isNoContent());
- //Main community has no collections, therefore contentType is not set
+ //Main community has no collections
getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString() + "/collections"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.totalElements", is(0)));
@@ -698,31 +888,30 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
//** GIVEN **
//1. A community-collection structure with one parent community with sub-community and one collection.
parentCommunity = CommunityBuilder.createCommunity(context)
- .withName("Parent Community")
- .withLogo("ThisIsSomeDummyText")
- .build();
+ .withName("Parent Community")
+ .withLogo("ThisIsSomeDummyText")
+ .build();
Community parentCommunity2 = CommunityBuilder.createCommunity(context)
- .withName("Parent Community 2")
- .withLogo("SomeTest")
- .build();
+ .withName("Parent Community 2")
+ .withLogo("SomeTest")
+ .build();
Community parentCommunityChild1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
- .withName("Sub Community")
- .build();
+ .withName("Sub Community")
+ .build();
Community parentCommunityChild2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
- .withName("Sub Community2")
- .build();
+ .withName("Sub Community2")
+ .build();
Community parentCommunityChild2Child1 = CommunityBuilder.createSubCommunity(context, parentCommunityChild2)
- .withName("Sub Sub Community")
- .build();
-
+ .withName("Sub Sub Community")
+ .build();
Community parentCommunity2Child1 = CommunityBuilder.createSubCommunity(context, parentCommunity2)
- .withName("Sub2 Community")
- .build();
+ .withName("Sub2 Community")
+ .build();
Collection col1 = CollectionBuilder.createCollection(context, parentCommunityChild1)
.withName("Collection 1")
@@ -730,98 +919,379 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
context.restoreAuthSystemState();
- getClient().perform(get("/api/core/communities/search/subCommunities")
- .param("parent", parentCommunity.getID().toString())
- .param("projection", "full"))
- .andExpect(status().isOk())
- .andExpect(content().contentType(contentType))
- //Checking that these communities are present
- .andExpect(jsonPath("$._embedded.communities", Matchers.containsInAnyOrder(
- CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild1.getName(),
- parentCommunityChild1.getID(),
- parentCommunityChild1.getHandle()),
- CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild2.getName(),
- parentCommunityChild2.getID(),
- parentCommunityChild2.getHandle())
- )))
- //Checking that these communities are not present
- .andExpect(jsonPath("$._embedded.communities", Matchers.not(Matchers.anyOf(
- CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity.getName(),
- parentCommunity.getID(),
- parentCommunity.getHandle()),
- CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity2.getName(),
- parentCommunity2.getID(),
- parentCommunity2.getHandle()),
- CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity2Child1.getName(),
- parentCommunity2Child1.getID(),
- parentCommunity2Child1.getHandle()),
- CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild2Child1.getName(),
- parentCommunityChild2Child1.getID(),
- parentCommunityChild2Child1.getHandle())
- ))))
- .andExpect(jsonPath("$._links.self.href",
- Matchers.containsString("/api/core/communities/search/subCommunities")))
- .andExpect(jsonPath("$.page.size", is(20)))
- .andExpect(jsonPath("$.page.totalElements", is(2)))
- ;
+ getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString() + "/subcommunities"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ //Checking that these communities are present
+ .andExpect(jsonPath("$._embedded.subcommunities", Matchers.containsInAnyOrder(
+ CommunityMatcher.matchCommunity(parentCommunityChild1),
+ CommunityMatcher.matchCommunity(parentCommunityChild2)
+ )))
+ //Checking that these communities are not present
+ .andExpect(jsonPath("$._embedded.subcommunities", Matchers.not(Matchers.anyOf(
+ CommunityMatcher.matchCommunity(parentCommunity),
+ CommunityMatcher.matchCommunity(parentCommunity2),
+ CommunityMatcher.matchCommunity(parentCommunity2Child1),
+ CommunityMatcher.matchCommunity(parentCommunityChild2Child1)
+ ))))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/core/communities/" + parentCommunity.getID().toString())))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", is(2)));
- getClient().perform(get("/api/core/communities/search/subCommunities")
- .param("parent", parentCommunityChild2.getID().toString())
- .param("projection", "full"))
- .andExpect(status().isOk())
- .andExpect(content().contentType(contentType))
- //Checking that these communities are present
- .andExpect(jsonPath("$._embedded.communities", Matchers.contains(
- CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild2Child1.getName(),
- parentCommunityChild2Child1.getID(),
- parentCommunityChild2Child1.getHandle())
- )))
- //Checking that these communities are not present
- .andExpect(jsonPath("$._embedded.communities", Matchers.not(Matchers.anyOf(
- CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity.getName(),
- parentCommunity.getID(),
- parentCommunity.getHandle()),
- CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity2.getName(),
- parentCommunity2.getID(),
- parentCommunity2.getHandle()),
- CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity2Child1.getName(),
- parentCommunity2Child1.getID(),
- parentCommunity2Child1.getHandle()),
- CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild2Child1.getName(),
- parentCommunityChild2Child1.getID(),
- parentCommunityChild2Child1.getHandle()),
- CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild1.getName(),
- parentCommunityChild1.getID(),
- parentCommunityChild1.getHandle())
- ))))
- .andExpect(jsonPath("$._links.self.href",
- Matchers.containsString("/api/core/communities/search/subCommunities")))
- .andExpect(jsonPath("$.page.size", is(20)))
- .andExpect(jsonPath("$.page.totalElements", is(1)))
- ;
+ getClient().perform(get("/api/core/communities/" + parentCommunityChild2.getID().toString() + "/subcommunities"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ //Checking that these communities are present
+ .andExpect(jsonPath("$._embedded.subcommunities", Matchers.contains(
+ CommunityMatcher.matchCommunity(parentCommunityChild2Child1)
+ )))
+ //Checking that these communities are not present
+ .andExpect(jsonPath("$._embedded.subcommunities", Matchers.not(Matchers.anyOf(
+ CommunityMatcher.matchCommunity(parentCommunity),
+ CommunityMatcher.matchCommunity(parentCommunity2),
+ CommunityMatcher.matchCommunity(parentCommunity2Child1),
+ CommunityMatcher.matchCommunity(parentCommunityChild2Child1),
+ CommunityMatcher.matchCommunity(parentCommunityChild1)
+ ))))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/core/communities/" + parentCommunityChild2.getID().toString())))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", is(1)));
- getClient().perform(get("/api/core/communities/search/subCommunities")
- .param("parent", parentCommunityChild2Child1.getID().toString()))
- .andExpect(status().isOk())
- .andExpect(content().contentType(contentType))
- .andExpect(jsonPath("$._links.self.href",
- Matchers.containsString("/api/core/communities/search/subCommunities")))
- .andExpect(jsonPath("$.page.size", is(20)))
- .andExpect(jsonPath("$.page.totalElements", is(0)))
- ;
+ getClient().perform(get("/api/core/communities/"
+ + parentCommunityChild2Child1.getID().toString() + "/subcommunities"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.containsString("/api/core/communities/" + parentCommunityChild2Child1.getID().toString())))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", is(0)));
}
@Test
- public void findAllSubCommunitiesWithoutUUID() throws Exception {
- getClient().perform(get("/api/core/communities/search/subCommunities"))
- .andExpect(status().isBadRequest());
+ public void findAllSubCommunitiesUnAuthenticatedTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .withLogo("ThisIsSomeDummyText")
+ .build();
+
+ Community communityChild1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+
+ Community communityChild2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community2")
+ .build();
+
+ Community communityChild2Child1 = CommunityBuilder.createSubCommunity(context, communityChild2)
+ .withName("Sub Community2")
+ .build();
+
+ resoucePolicyService.removePolicies(context, communityChild2, Constants.READ);
+ context.restoreAuthSystemState();
+
+ // anonymous can NOT see the private communities
+ getClient().perform(get("/api/core/communities/" + communityChild2.getID().toString() + "/subcommunities"))
+ .andExpect(status().isUnauthorized());
+
+ // anonymous can see only public communities
+ getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString() + "/subcommunities"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.subcommunities", Matchers.contains(
+ CommunityMatcher.matchCommunity(communityChild1))))
+ .andExpect(jsonPath("$.page.totalElements", is(1)));
+
+ // admin can see all communities
+ String tokenAdmin = getAuthToken(admin.getEmail(), password);
+ getClient(tokenAdmin).perform(get("/api/core/communities/"
+ + parentCommunity.getID().toString() + "/subcommunities"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.subcommunities", Matchers.containsInAnyOrder(
+ CommunityMatcher.matchCommunity(communityChild1),
+ CommunityMatcher.matchCommunity(communityChild2))))
+ .andExpect(jsonPath("$.page.totalElements", is(2)));
+ }
+
+ @Test
+ public void findAllSubCommunitiesForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .withLogo("ThisIsSomeDummyText")
+ .build();
+
+ Community communityChild1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+
+ Community communityChild2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community2")
+ .build();
+
+ Community communityChild2Child1 = CommunityBuilder.createSubCommunity(context, communityChild2)
+ .withName("Sub Community2")
+ .build();
+
+ resoucePolicyService.removePolicies(context, communityChild2, Constants.READ);
+ context.restoreAuthSystemState();
+
+ String tokenEperson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEperson).perform(get("/api/core/communities/"
+ + parentCommunity.getID().toString() + "/subcommunities"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.subcommunities", Matchers.contains(
+ CommunityMatcher.matchCommunity(communityChild1))))
+ .andExpect(jsonPath("$.page.totalElements", is(1)));
+
+ getClient(tokenEperson).perform(get("/api/core/communities/"
+ + communityChild2.getID().toString() + "/subcommunities"))
+ .andExpect(status().isForbidden());
+ }
+
+ @Test
+ public void findAllSubCommunitiesGrantAccessAdminsTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ EPerson parentComAdmin = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson1@mail.com")
+ .withPassword("qwerty01")
+ .build();
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .withLogo("ThisIsSomeDummyText")
+ .withAdminGroup(parentComAdmin)
+ .build();
+
+ EPerson child1Admin = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson2@mail.com")
+ .withPassword("qwerty02")
+ .build();
+ Community communityChild1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .withAdminGroup(child1Admin)
+ .build();
+
+ EPerson child2Admin = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson3@mail.com")
+ .withPassword("qwerty03")
+ .build();
+ Community communityChild2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community2")
+ .withAdminGroup(child2Admin)
+ .build();
+
+ Community ommunityChild1Child1 = CommunityBuilder.createSubCommunity(context, communityChild1)
+ .withName("Sub1 Community 1")
+ .build();
+
+ Community сommunityChild2Child1 = CommunityBuilder.createSubCommunity(context, communityChild2)
+ .withName("Sub2 Community 1")
+ .build();
+
+ resoucePolicyService.removePolicies(context, parentCommunity, Constants.READ);
+ resoucePolicyService.removePolicies(context, communityChild1, Constants.READ);
+ resoucePolicyService.removePolicies(context, communityChild2, Constants.READ);
+
+ context.restoreAuthSystemState();
+
+ String tokenParentAdmin = getAuthToken(parentComAdmin.getEmail(), "qwerty01");
+ getClient(tokenParentAdmin).perform(get("/api/core/communities/"
+ + parentCommunity.getID().toString() + "/subcommunities"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$._embedded.subcommunities", Matchers.containsInAnyOrder(
+ CommunityMatcher.matchCommunity(communityChild1),
+ CommunityMatcher.matchCommunity(communityChild2))))
+ .andExpect(jsonPath("$.page.totalElements", is(2)));
+
+ String tokenChild1Admin = getAuthToken(child1Admin.getEmail(), "qwerty02");
+ getClient(tokenChild1Admin).perform(get("/api/core/communities/"
+ + parentCommunity.getID().toString() + "/subcommunities"))
+ .andExpect(status().isForbidden());
+
+ String tokenChild2Admin = getAuthToken(child2Admin.getEmail(), "qwerty03");
+ getClient(tokenChild2Admin).perform(get("/api/core/communities/"
+ + communityChild1.getID().toString() + "/subcommunities"))
+ .andExpect(status().isForbidden());
+ }
+
+ @Test
+ public void findAllCollectionsUnAuthenticatedTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .withLogo("ThisIsSomeDummyText")
+ .build();
+
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+
+ Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+
+ Collection child1Col1 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 1 child 1")
+ .build();
+ Collection child1Col2 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 2 child 1")
+ .build();
+
+ Collection child2Col1 = CollectionBuilder.createCollection(context, child2)
+ .withName("Collection 1 child 2")
+ .build();
+
+ resoucePolicyService.removePolicies(context, child1Col2, Constants.READ);
+ resoucePolicyService.removePolicies(context, child2, Constants.READ);
+ context.restoreAuthSystemState();
+
+ // anonymous can see only public communities
+ getClient().perform(get("/api/core/communities/" + child1.getID().toString() + "/collections"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.collections", Matchers.contains(CollectionMatcher
+ .matchCollection(child1Col1))))
+ .andExpect(jsonPath("$.page.totalElements", is(1)));
+
+ // anonymous can NOT see the private communities
+ getClient().perform(get("/api/core/communities/" + child2.getID().toString() + "/collections"))
+ .andExpect(status().isUnauthorized());
+ }
+
+ @Test
+ public void findAllCollectionsForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .withLogo("ThisIsSomeDummyText")
+ .build();
+
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community 1")
+ .build();
+
+
+ Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community 2")
+ .build();
+
+ Collection child1Col1 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 1 child 1")
+ .build();
+ Collection child1Col2 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 2 child 1")
+ .build();
+
+ Collection child2Col1 = CollectionBuilder.createCollection(context, child2)
+ .withName("Collection 1 child 2")
+ .build();
+
+ resoucePolicyService.removePolicies(context, child1Col2, Constants.READ);
+ resoucePolicyService.removePolicies(context, child2, Constants.READ);
+ context.restoreAuthSystemState();
+
+ String tokenAdmin = getAuthToken(admin.getEmail(), password);
+ getClient(tokenAdmin).perform(get("/api/core/communities/" + child1.getID().toString() + "/collections"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.collections", Matchers.containsInAnyOrder(
+ CollectionMatcher.matchCollection(child1Col1),
+ CollectionMatcher.matchCollection(child1Col2))))
+ .andExpect(jsonPath("$.page.totalElements", is(2)));
+
+ getClient(tokenAdmin).perform(get("/api/core/communities/" + child2.getID().toString() + "/collections"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.collections", Matchers.contains(
+ CollectionMatcher.matchCollection(child2Col1))))
+ .andExpect(jsonPath("$.page.totalElements", is(1)));
+
+ String tokenEperson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEperson).perform(get("/api/core/communities/" + child2.getID().toString() + "/collections"))
+ .andExpect(status().isForbidden());
+ }
+
+ @Test
+ public void findAllCollectionsGrantAccessAdminsTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ EPerson parentAdmin = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson1@mail.com")
+ .withPassword("qwerty01")
+ .build();
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .withLogo("ThisIsSomeDummyText")
+ .withAdminGroup(parentAdmin)
+ .build();
+
+ EPerson child1Admin = EPersonBuilder.createEPerson(context)
+ .withEmail("child1admin@mail.com")
+ .withPassword("qwerty02")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .withAdminGroup(child1Admin)
+ .build();
+
+ EPerson child2Admin = EPersonBuilder.createEPerson(context)
+ .withEmail("child2admin@mail.com")
+ .withPassword("qwerty03")
+ .build();
+ Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .withAdminGroup(child2Admin)
+ .build();
+
+ Collection child1Col1 = CollectionBuilder.createCollection(context, child1)
+ .withName("Child 1 Collection 1")
+ .build();
+ Collection child1Col2 = CollectionBuilder.createCollection(context, child1)
+ .withName("Child 1 Collection 2")
+ .build();
+
+ Collection child2Col1 = CollectionBuilder.createCollection(context, child2)
+ .withName("Child 2 Collection 1")
+ .build();
+
+ resoucePolicyService.removePolicies(context, child1Col2, Constants.READ);
+ resoucePolicyService.removePolicies(context, child2, Constants.READ);
+ context.restoreAuthSystemState();
+
+ String tokenParentAdmin = getAuthToken(parentAdmin.getEmail(), "qwerty01");
+ getClient(tokenParentAdmin).perform(get("/api/core/communities/" + child1.getID().toString() + "/collections"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.collections", Matchers.containsInAnyOrder(
+ CollectionMatcher.matchCollection(child1Col1),
+ CollectionMatcher.matchCollection(child1Col2))))
+ .andExpect(jsonPath("$.page.totalElements", is(2)));
+
+
+ getClient(tokenParentAdmin).perform(get("/api/core/communities/" + child2.getID().toString() + "/collections"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.collections", Matchers.contains(
+ CollectionMatcher.matchCollection(child2Col1))))
+ .andExpect(jsonPath("$.page.totalElements", is(1)));
+
+ String tokenChild2Admin = getAuthToken(child2Admin.getEmail(), "qwerty03");
+ getClient(tokenChild2Admin).perform(get("/api/core/communities/" + child1.getID().toString() + "/collections"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.collections", Matchers.contains(
+ CollectionMatcher.matchCollection(child1Col1))))
+ .andExpect(jsonPath("$.page.totalElements", is(1)));
}
@Test
public void findAllSubCommunitiesWithUnexistentUUID() throws Exception {
- getClient().perform(get("/api/core/communities/search/subCommunities")
- .param("parent", UUID.randomUUID().toString()))
- .andExpect(status().isNotFound());
+ getClient().perform(get("/api/core/communities/" + UUID.randomUUID().toString() + "/subcommunities"))
+ .andExpect(status().isNotFound());
}
@Test
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java
index 9cbb163800..9610287273 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java
@@ -235,6 +235,28 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest {
}
+ @Test
+ public void findOneForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ EPerson ePerson1 = EPersonBuilder.createEPerson(context)
+ .withNameInMetadata("Mik", "Reck")
+ .withEmail("MikReck@email.com")
+ .withPassword("qwerty01")
+ .build();
+
+ EPerson ePerson2 = EPersonBuilder.createEPerson(context)
+ .withNameInMetadata("Bob", "Smith")
+ .withEmail("bobsmith@fake-email.com")
+ .build();
+
+ context.restoreAuthSystemState();
+
+ String tokenEperson1 = getAuthToken(ePerson1.getEmail(), "qwerty01");
+ getClient(tokenEperson1).perform(get("/api/eperson/epersons/" + ePerson2.getID()))
+ .andExpect(status().isForbidden());
+ }
+
@Test
public void readEpersonAuthorizationTest() throws Exception {
context.turnOffAuthorisationSystem();
@@ -1494,6 +1516,31 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest {
new MetadataPatchSuite().runWith(getClient(token), "/api/eperson/epersons/" + ePerson.getID(), expectedStatus);
}
+ @Test
+ public void newlyCreatedAccountHasNoGroups() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ EPerson ePerson1 = EPersonBuilder.createEPerson(context)
+ .withNameInMetadata("Mik", "Reck")
+ .withEmail("MikReck@email.com")
+ .withPassword("qwerty01")
+ .build();
+
+ context.restoreAuthSystemState();
+
+ String tokenEperson1 = getAuthToken(ePerson1.getEmail(), "qwerty01");
+ // by contract the groups embedded in the eperson only contains direct explicit membership,
+ // so the anonymous group is not listed
+ getClient(tokenEperson1).perform(get("/api/eperson/epersons/" + ePerson1.getID())
+ .param("projection", "full"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$", Matchers.allOf(
+ hasJsonPath("$._embedded.groups._embedded.groups.length()", is(0)),
+ hasJsonPath("$._embedded.groups.page.totalElements", is(0))
+ )));
+ }
+
/**
* Test that epersons/<:uuid>/groups endpoint returns the direct groups of the epersons
* @throws Exception
@@ -1543,5 +1590,4 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest {
);
}
-
}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java
index 3174b96bc4..0801e9d63a 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java
@@ -43,11 +43,13 @@ import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.model.patch.ReplaceOperation;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.app.rest.test.MetadataPatchSuite;
+import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.CommunityService;
+import org.dspace.core.Constants;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.eperson.factory.EPersonServiceFactory;
@@ -55,6 +57,7 @@ import org.dspace.eperson.service.EPersonService;
import org.dspace.eperson.service.GroupService;
import org.hamcrest.Matchers;
import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
/**
* @author Jonas Van Goolen - (jonas@atmire.com)
@@ -62,6 +65,9 @@ import org.junit.Test;
public class GroupRestRepositoryIT extends AbstractControllerIntegrationTest {
+ @Autowired
+ ResourcePolicyService resourcePolicyService;
+
@Test
public void createTest()
throws Exception {
@@ -195,6 +201,13 @@ public class GroupRestRepositoryIT extends AbstractControllerIntegrationTest {
;
}
+ @Test
+ public void findAllForbiddenTest() throws Exception {
+ String tokenEperson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEperson).perform(get("/api/eperson/groups"))
+ .andExpect(status().isForbidden());
+ }
+
@Test
public void findAllPaginationTest() throws Exception {
@@ -304,6 +317,22 @@ public class GroupRestRepositoryIT extends AbstractControllerIntegrationTest {
Matchers.containsString("/api/eperson/groups/" + group2.getID())));
}
+ @Test
+ public void findOneForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ Group privateGroup = GroupBuilder.createGroup(context)
+ .withName("Private Group")
+ .build();
+
+ resourcePolicyService.removePolicies(context, privateGroup, Constants.READ);
+ context.restoreAuthSystemState();
+
+ String tokenEperson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEperson).perform(get("/api/eperson/groups/" + privateGroup.getID()))
+ .andExpect(status().isForbidden());
+ }
+
@Test
public void findOneTestWrongUUID() throws Exception {
context.turnOffAuthorisationSystem();
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestControllerIT.java
index 9fac87a9e6..1c76121811 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestControllerIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestControllerIT.java
@@ -283,4 +283,34 @@ public class ItemOwningCollectionUpdateRestControllerIT extends AbstractControll
}
+
+ @Test
+ public void moveItemForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Collection 1")
+ .build();
+ Collection col2 = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Collection 2")
+ .build();
+
+ Item publicItem1 = ItemBuilder.createItem(context, col1)
+ .withTitle("Public item 1")
+ .withIssueDate("2019-10-21")
+ .withAuthor("Smith, Donald")
+ .build();
+
+ context.restoreAuthSystemState();
+
+ String tokenEPerson = getAuthToken(eperson.getEmail(), password);
+
+ getClient(tokenEPerson).perform(put("/api/core/items/" + publicItem1.getID() + "/owningCollection/")
+ .contentType(parseMediaType(TEXT_URI_LIST_VALUE))
+ .content("https://localhost:8080/spring-rest/api/core/collections/" + col2.getID()))
+ .andExpect(status().isForbidden());
+ }
}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java
index 45c398ba85..35b54729ec 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java
@@ -8,7 +8,6 @@
package org.dspace.app.rest;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
-
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@@ -124,6 +123,13 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
;
}
+ @Test
+ public void findAllForbiddenTest() throws Exception {
+ String tokenEperson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEperson).perform(get("/api/core/items"))
+ .andExpect(status().isForbidden());
+ }
+
@Test
public void findAllWithPaginationTest() throws Exception {
context.turnOffAuthorisationSystem();
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemTemplateRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemTemplateRestControllerIT.java
index 2f8839f74e..58cca9c414 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemTemplateRestControllerIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemTemplateRestControllerIT.java
@@ -32,14 +32,20 @@ import org.dspace.app.rest.model.patch.AddOperation;
import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.model.patch.ReplaceOperation;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
+import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.Collection;
+import org.dspace.core.Constants;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.MvcResult;
public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationTest {
+ @Autowired
+ ResourcePolicyService resourcePolicyService;
+
private ObjectMapper mapper;
private String adminAuthToken;
private Collection childCollection;
@@ -160,6 +166,17 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT
)));
}
+ @Test
+ public void getTemplateItemFromCollectionForbiddenTest() throws Exception {
+ setupTestTemplate();
+ String itemUuidString = installTestTemplate();
+
+ resourcePolicyService.removePolicies(context, childCollection, Constants.READ);
+ String tokenEperson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEperson).perform(get(getCollectionTemplateItemUrlTemplate(childCollection.getID().toString())))
+ .andExpect(status().isForbidden());
+ }
+
@Test
public void getTemplateItemFromItemId() throws Exception {
setupTestTemplate();
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java
index c08cef9289..4ce86797f0 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java
@@ -8,7 +8,6 @@
package org.dspace.app.rest;
import static com.jayway.jsonpath.JsonPath.read;
-
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
@@ -27,11 +26,9 @@ import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
-
import javax.ws.rs.core.MediaType;
import com.fasterxml.jackson.databind.ObjectMapper;
-
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.builder.EPersonBuilder;
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ShibbolethRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ShibbolethRestControllerIT.java
new file mode 100644
index 0000000000..a690e539ab
--- /dev/null
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ShibbolethRestControllerIT.java
@@ -0,0 +1,42 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
+import org.junit.Test;
+
+/**
+ * Integration test that cover ShibbolethRestController
+ *
+ * @author Giuseppe Digilio (giuseppe dot digilio at 4science dot it)
+ */
+public class ShibbolethRestControllerIT extends AbstractControllerIntegrationTest {
+
+ @Test
+ public void testRedirectToDefaultDspaceUrl() throws Exception {
+ String token = getAuthToken(eperson.getEmail(), password);
+
+ getClient(token).perform(get("/api/authn/shibboleth"))
+ .andExpect(status().is3xxRedirection())
+ .andExpect(redirectedUrl("http://localhost:3000"));
+ }
+
+ @Test
+ public void testRedirectToGivenUrl() throws Exception {
+ String token = getAuthToken(eperson.getEmail(), password);
+
+ getClient(token).perform(get("/api/authn/shibboleth")
+ .param("redirectUrl", "http://dspace.org"))
+ .andExpect(status().is3xxRedirection())
+ .andExpect(redirectedUrl("http://dspace.org"));
+ }
+}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java
index c9e07b9b0b..38d22fdf04 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java
@@ -8,6 +8,7 @@
package org.dspace.app.rest;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
+import static org.dspace.app.rest.test.AbstractControllerIntegrationTest.REST_SERVER_URL;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.hasSize;
@@ -62,6 +63,22 @@ public class SubmissionDefinitionsControllerIT extends AbstractControllerIntegra
;
}
+ @Test
+ public void findAllWithNewlyCreatedAccountTest() throws Exception {
+ String token = getAuthToken(eperson.getEmail(), password);
+ getClient(token).perform(get("/api/config/submissiondefinitions"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", greaterThanOrEqualTo(1)))
+ .andExpect(jsonPath("$.page.totalPages", greaterThanOrEqualTo(1)))
+ .andExpect(jsonPath("$.page.number", is(0)))
+ .andExpect(jsonPath("$._links.search.href", is(REST_SERVER_URL
+ + "config/submissiondefinitions/search")))
+ //The array of browse index should have a size greater or equals to 1
+ .andExpect(jsonPath("$._embedded.submissiondefinitions", hasSize(greaterThanOrEqualTo(1))));
+ }
+
@Test
public void findDefault() throws Exception {
getClient().perform(get("/api/config/submissiondefinitions/traditional"))
@@ -84,6 +101,19 @@ public class SubmissionDefinitionsControllerIT extends AbstractControllerIntegra
;
}
+ @Test
+ public void findOneWithNewlyCreatedAccountTest() throws Exception {
+ String tokenEPerson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEPerson).perform(get("/api/config/submissiondefinitions/traditional"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$", allOf(
+ hasJsonPath("$.isDefault", is(true)),
+ hasJsonPath("$.name", is("traditional")),
+ hasJsonPath("$.id", is("traditional")),
+ hasJsonPath("$.type", is("submissiondefinition")))));
+ }
+
@Test
public void findByCollection() throws Exception {
@@ -117,6 +147,29 @@ public class SubmissionDefinitionsControllerIT extends AbstractControllerIntegra
.matchSubmissionDefinition(true, "traditional", "traditional")));
}
+ @Test
+ public void findByCollectionWithNewlyCreatedAccountTest() throws Exception {
+
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Community")
+ .build();
+
+ Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Collection 1")
+ .build();
+
+ String token = getAuthToken(eperson.getEmail(), password);
+ getClient(token).perform(get("/api/config/submissiondefinitions/search/findByCollection")
+ .param("uuid", col1.getID().toString()))
+ .andExpect(status().isOk())
+ .andDo(MockMvcResultHandlers.print())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$", SubmissionDefinitionsMatcher
+ .matchSubmissionDefinition(true, "traditional", "traditional")));
+ }
+
@Test
public void findCollections() throws Exception {
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionFormsControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionFormsControllerIT.java
index f024d38c92..b17ea47c2d 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionFormsControllerIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionFormsControllerIT.java
@@ -55,6 +55,21 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
;
}
+ @Test
+ public void findAllWithNewlyCreatedAccountTest() throws Exception {
+ String token = getAuthToken(eperson.getEmail(), password);
+ getClient(token).perform(get("/api/config/submissionforms"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", equalTo(4)))
+ .andExpect(jsonPath("$.page.totalPages", equalTo(1)))
+ .andExpect(jsonPath("$.page.number", is(0)))
+ .andExpect(jsonPath("$._links.self.href", Matchers.startsWith(REST_SERVER_URL
+ + "config/submissionforms")))
+ .andExpect(jsonPath("$._embedded.submissionforms", hasSize(equalTo(4))));
+ }
+
@Test
public void findTraditionalPageOne() throws Exception {
//When we call the root endpoint as anonymous user
@@ -96,6 +111,34 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
;
}
+ @Test
+ public void findTraditionalPageOneWithNewlyCreatedAccountTest() throws Exception {
+ String token = getAuthToken(eperson.getEmail(), password);
+ getClient(token).perform(get("/api/config/submissionforms/traditionalpageone"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$.id", is("traditionalpageone")))
+ .andExpect(jsonPath("$.name", is("traditionalpageone")))
+ .andExpect(jsonPath("$.type", is("submissionform")))
+ .andExpect(jsonPath("$._links.self.href", Matchers
+ .startsWith(REST_SERVER_URL + "config/submissionforms/traditionalpageone")))
+ .andExpect(jsonPath("$.rows[0].fields", contains(
+ SubmissionFormFieldMatcher.matchFormFieldDefinition("name", "Author",
+ null, true,"Add an author", "dc.contributor.author"))))
+ .andExpect(jsonPath("$.rows[1].fields", contains(
+ SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Title",
+ "You must enter a main title for this item.", false,
+ "Enter the main title of the item.", "dc.title"))))
+ .andExpect(jsonPath("$.rows[3].fields",contains(
+ SubmissionFormFieldMatcher.matchFormFieldDefinition("date", "Date of Issue",
+ "You must enter at least the year.", false,
+ "Please give the date", "col-sm-4",
+ "dc.date.issued"),
+ SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Publisher",
+ null, false,"Enter the name of",
+ "col-sm-8","dc.publisher"))));
+ }
+
@Test
public void findOpenRelationshipConfig() throws Exception {
String token = getAuthToken(admin.getEmail(), password);
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionSectionsControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionSectionsControllerIT.java
index 643debcbfc..7a832d3e3d 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionSectionsControllerIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionSectionsControllerIT.java
@@ -33,7 +33,7 @@ public class SubmissionSectionsControllerIT extends AbstractControllerIntegratio
.andExpect(status().isUnauthorized());
- String token = getAuthToken(admin.getEmail(), password);
+ String token = getAuthToken(eperson.getEmail(), password);
//When we call the root endpoint
getClient(token).perform(get("/api/config/submissionsections"))
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionUploadsControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionUploadsControllerIT.java
index 07e570f609..9ffd27c820 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionUploadsControllerIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionUploadsControllerIT.java
@@ -54,4 +54,19 @@ public class SubmissionUploadsControllerIT extends AbstractControllerIntegration
.andExpect(jsonPath("$._embedded.submissionuploads", hasSize(greaterThanOrEqualTo(1))))
;
}
+
+ @Test
+ public void findAllWithNewlyCreatedAccountTest() throws Exception {
+ String token = getAuthToken(eperson.getEmail(), password);
+ getClient(token).perform(get("/api/config/submissionuploads"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(contentType))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", greaterThanOrEqualTo(1)))
+ .andExpect(jsonPath("$.page.totalPages", greaterThanOrEqualTo(1)))
+ .andExpect(jsonPath("$.page.number", is(0)))
+ .andExpect(jsonPath("$._links.self.href",
+ Matchers.startsWith(REST_SERVER_URL + "config/submissionuploads")))
+ .andExpect(jsonPath("$._embedded.submissionuploads", hasSize(greaterThanOrEqualTo(1))));
+ }
}
\ No newline at end of file
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VersionHistoryRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VersionHistoryRestRepositoryIT.java
new file mode 100644
index 0000000000..584b099e9e
--- /dev/null
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VersionHistoryRestRepositoryIT.java
@@ -0,0 +1,162 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import java.sql.SQLException;
+import java.util.Date;
+
+import org.dspace.app.rest.builder.CollectionBuilder;
+import org.dspace.app.rest.builder.CommunityBuilder;
+import org.dspace.app.rest.builder.ItemBuilder;
+import org.dspace.app.rest.matcher.VersionHistoryMatcher;
+import org.dspace.app.rest.matcher.VersionMatcher;
+import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
+import org.dspace.authorize.AuthorizeException;
+import org.dspace.content.Collection;
+import org.dspace.content.Community;
+import org.dspace.content.Item;
+import org.dspace.services.ConfigurationService;
+import org.dspace.versioning.Version;
+import org.dspace.versioning.VersionHistory;
+import org.dspace.versioning.service.VersionHistoryService;
+import org.dspace.versioning.service.VersioningService;
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class VersionHistoryRestRepositoryIT extends AbstractControllerIntegrationTest {
+
+
+ VersionHistory versionHistory;
+ Item item;
+ Version version;
+
+ @Autowired
+ private ConfigurationService configurationService;
+
+ @Autowired
+ private VersionHistoryService versionHistoryService;
+
+ @Autowired
+ private VersioningService versioningService;
+
+ @Before
+ public void setup() throws SQLException, AuthorizeException {
+ context.turnOffAuthorisationSystem();
+ versionHistory = versionHistoryService.create(context);
+ //** GIVEN **
+ //1. A community-collection structure with one parent community with sub-community and two collections.
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
+ Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
+
+ //2. Three public items that are readable by Anonymous with different subjects
+ item = ItemBuilder.createItem(context, col1)
+ .withTitle("Public item 1")
+ .withIssueDate("2017-10-17")
+ .withAuthor("Smith, Donald").withAuthor("Doe, John")
+ .withSubject("ExtraEntry")
+ .build();
+ version = versioningService.createNewVersion(context, versionHistory, item, "test", new Date(), 0);
+ context.restoreAuthSystemState();
+ }
+
+ @Test
+ public void findOneTest() throws Exception {
+ getClient().perform(get("/api/versioning/versionhistories/" + versionHistory.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$", is(VersionHistoryMatcher.matchEntry(versionHistory))));
+
+ }
+
+
+ @Test
+ public void findOneForbiddenTest() throws Exception {
+
+ configurationService.setProperty("versioning.item.history.view.admin", true);
+
+ String token = getAuthToken(eperson.getEmail(), password);
+ getClient(token).perform(get("/api/versioning/versionhistories/" + versionHistory.getID()))
+ .andExpect(status().isForbidden());
+ configurationService.setProperty("versioning.item.history.view.admin", false);
+ }
+
+ @Test
+ public void findOneWrongIDTest() throws Exception {
+ int wrongVersionHistoryId = (versionHistory.getID() + 5) * 57;
+ getClient().perform(get("/api/versioning/versionhistories/" + wrongVersionHistoryId))
+ .andExpect(status().isNotFound());
+
+ }
+ @Test
+ public void findVersionsOneWrongIDTest() throws Exception {
+ int wrongVersionHistoryId = (versionHistory.getID() + 5) * 57;
+ getClient().perform(get("/api/versioning/versionhistories/" + wrongVersionHistoryId + "/versions"))
+ .andExpect(status().isNotFound());
+
+ }
+
+ @Test
+ public void findVersionsOfVersionHistoryTest() throws Exception {
+ Version version = versionHistoryService.getFirstVersion(context, versionHistory);
+ getClient().perform(get("/api/versioning/versionhistories/" + versionHistory.getID() + "/versions"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.versions", contains(VersionMatcher.matchEntry(version))));
+
+ context.turnOffAuthorisationSystem();
+ Version secondVersion = versioningService
+ .createNewVersion(context, versionHistory, item, "test", new Date(), 0);
+ context.restoreAuthSystemState();
+
+ getClient().perform(get("/api/versioning/versionhistories/" + versionHistory.getID() + "/versions"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.versions", containsInAnyOrder(VersionMatcher.matchEntry(version),
+ VersionMatcher
+ .matchEntry(secondVersion))));
+
+ }
+ @Test
+ public void findVersionsOfVersionHistoryPaginationTest() throws Exception {
+
+ context.turnOffAuthorisationSystem();
+ Version version = versionHistoryService.getFirstVersion(context, versionHistory);
+ Version secondVersion = versioningService
+ .createNewVersion(context, versionHistory, item, "test", new Date(), 0);
+ context.restoreAuthSystemState();
+
+ getClient().perform(get("/api/versioning/versionhistories/" + versionHistory.getID() + "/versions")
+ .param("size", "1"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.versions", contains(VersionMatcher.matchEntry(secondVersion))))
+ .andExpect(jsonPath("$._embedded.versions",
+ Matchers.not(contains(VersionMatcher.matchEntry(version)))));
+ getClient().perform(get("/api/versioning/versionhistories/" + versionHistory.getID() + "/versions")
+ .param("size", "1")
+ .param("page", "1"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.versions", contains(VersionMatcher.matchEntry(version))))
+ .andExpect(jsonPath("$._embedded.versions",
+ Matchers.not(contains(VersionMatcher.matchEntry(secondVersion)))));
+
+ }
+
+}
+
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VersionRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VersionRestRepositoryIT.java
new file mode 100644
index 0000000000..c158dde5f0
--- /dev/null
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VersionRestRepositoryIT.java
@@ -0,0 +1,192 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.app.rest;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.dspace.app.rest.builder.CollectionBuilder;
+import org.dspace.app.rest.builder.CommunityBuilder;
+import org.dspace.app.rest.builder.ItemBuilder;
+import org.dspace.app.rest.matcher.ItemMatcher;
+import org.dspace.app.rest.matcher.VersionMatcher;
+import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
+import org.dspace.content.Collection;
+import org.dspace.content.Community;
+import org.dspace.content.Item;
+import org.dspace.content.service.ItemService;
+import org.dspace.services.ConfigurationService;
+import org.dspace.versioning.Version;
+import org.dspace.versioning.service.VersioningService;
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class VersionRestRepositoryIT extends AbstractControllerIntegrationTest {
+
+ Item item;
+ Version version;
+
+ @Autowired
+ private ItemService itemService;
+
+ @Autowired
+ private VersioningService versioningService;
+
+ @Autowired
+ private ConfigurationService configurationService;
+
+ @Before
+ public void setup() {
+ context.turnOffAuthorisationSystem();
+
+ //** GIVEN **
+ //1. A community-collection structure with one parent community with sub-community and two collections.
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
+ Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
+
+ //2. Three public items that are readable by Anonymous with different subjects
+ item = ItemBuilder.createItem(context, col1)
+ .withTitle("Public item 1")
+ .withIssueDate("2017-10-17")
+ .withAuthor("Smith, Donald").withAuthor("Doe, John")
+ .withSubject("ExtraEntry")
+ .build();
+ version = versioningService.createNewVersion(context, item);
+ context.restoreAuthSystemState();
+ }
+
+ @Test
+ public void findOneTest() throws Exception {
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+
+ getClient(adminToken).perform(get("/api/versioning/versions/" + version.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$", Matchers.is(VersionMatcher.matchEntry(version))));
+ }
+
+ @Test
+ public void findOneSubmitterNameVisisbleTest() throws Exception {
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+
+ getClient(adminToken).perform(get("/api/versioning/versions/" + version.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$", Matchers.is(VersionMatcher.matchEntry(version))))
+ .andExpect(jsonPath("$.submitterName", Matchers.is(version.getEPerson().getFullName())));
+ }
+
+ @Test
+ public void findOneSubmitterNameConfigurationPropertyFalseAdminUserLinkVisibleTest() throws Exception {
+
+ configurationService.setProperty("versioning.item.history.include.submitter", false);
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+
+ getClient(adminToken).perform(get("/api/versioning/versions/" + version.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$", Matchers.is(VersionMatcher.matchEntry(version))))
+ .andExpect(jsonPath("$.submitterName", Matchers.is(version.getEPerson().getFullName())));
+
+ configurationService.setProperty("versioning.item.history.include.submitter", true);
+
+ }
+
+ @Test
+ public void findOneSubmitterNameConfigurationPropertyTrueNormalUserLinkVisibleTest() throws Exception {
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+
+ getClient(adminToken).perform(get("/api/versioning/versions/" + version.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$", Matchers.is(VersionMatcher.matchEntry(version))))
+ .andExpect(jsonPath("$.submitterName", Matchers.is(version.getEPerson().getFullName())));
+
+ }
+
+ @Test
+ public void findOneSubmitterNameConfigurationPropertyTrueAnonUserLinkVisibleTest() throws Exception {
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+
+ getClient(adminToken).perform(get("/api/versioning/versions/" + version.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$", Matchers.is(VersionMatcher.matchEntry(version))))
+ .andExpect(jsonPath("$.submitterName", Matchers.is(version.getEPerson().getFullName())));
+
+ }
+
+ @Test
+ public void findOneSubmitterNameConfigurationPropertyFalseNormalUserLinkInvisibleTest() throws Exception {
+
+ configurationService.setProperty("versioning.item.history.include.submitter", false);
+
+ String adminToken = getAuthToken(eperson.getEmail(), password);
+
+ getClient(adminToken).perform(get("/api/versioning/versions/" + version.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$", Matchers.is(VersionMatcher.matchEntry(version))))
+ .andExpect(jsonPath("$.submitterName").doesNotExist());
+
+ configurationService.setProperty("versioning.item.history.include.submitter", true);
+
+ }
+ @Test
+ public void findOneUnauthorizedTest() throws Exception {
+
+ getClient().perform(get("/api/versioning/versions/" + version.getID()))
+ .andExpect(status().isUnauthorized());
+ }
+
+ @Test
+ public void findOneForbiddenTest() throws Exception {
+
+ configurationService.setProperty("versioning.item.history.view.admin", true);
+
+ String token = getAuthToken(eperson.getEmail(), password);
+ getClient(token).perform(get("/api/versioning/versions/" + version.getID()))
+ .andExpect(status().isForbidden());
+ configurationService.setProperty("versioning.item.history.view.admin", false);
+ }
+
+ @Test
+ public void versionForItemTest() throws Exception {
+
+ getClient().perform(get("/api/core/items/" + version.getItem().getID() + "/version"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$", Matchers.is(VersionMatcher.matchEntry(version))));
+ }
+
+ @Test
+ public void versionItemTest() throws Exception {
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+
+ getClient(adminToken).perform(get("/api/versioning/versions/" + version.getID() + "/item"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$", Matchers.is(ItemMatcher.matchItemProperties(version.getItem()))));
+ }
+
+ @Test
+ public void versionItemTestWrongId() throws Exception {
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+
+ getClient(adminToken).perform(get("/api/versioning/versions/" + ((version.getID() + 5) * 57) + "/item"))
+ .andExpect(status().isNotFound());
+ }
+}
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ViewEventRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ViewEventRestRepositoryIT.java
index 4a5b1f1d68..a84b2138a1 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ViewEventRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ViewEventRestRepositoryIT.java
@@ -453,7 +453,7 @@ public class ViewEventRestRepositoryIT extends AbstractControllerIntegrationTest
@Test
- public void postTestAuthenticatedUserSucces() throws Exception {
+ public void postTestAuthenticatedUserSuccess() throws Exception {
context.turnOffAuthorisationSystem();
@@ -485,7 +485,7 @@ public class ViewEventRestRepositoryIT extends AbstractControllerIntegrationTest
ObjectMapper mapper = new ObjectMapper();
- String token = getAuthToken(admin.getEmail(), password);
+ String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(post("/api/statistics/viewevents")
.content(mapper.writeValueAsBytes(viewEventRest))
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java
index 5cb79fba2c..dd161819f6 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java
@@ -24,7 +24,6 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
-
import javax.ws.rs.core.MediaType;
import org.apache.commons.io.IOUtils;
@@ -34,6 +33,7 @@ import org.dspace.app.rest.builder.ClaimedTaskBuilder;
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.builder.EPersonBuilder;
+import org.dspace.app.rest.builder.ItemBuilder;
import org.dspace.app.rest.builder.WorkflowItemBuilder;
import org.dspace.app.rest.builder.WorkspaceItemBuilder;
import org.dspace.app.rest.matcher.CollectionMatcher;
@@ -668,15 +668,15 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
.andExpect(status().is(200));
// a workspaceitem should exist now in the submitter workspace
- getClient(tokenSubmitter).perform(get("/api/submission/workspaceitems"))
- .andExpect(status().isOk())
- .andExpect(jsonPath("$._embedded.workspaceitems", Matchers.containsInAnyOrder(
- WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(null, "Workflow Item 1",
- "2017-10-17"))))
- .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/submission/workspaceitems")))
- .andExpect(jsonPath("$.page.size", is(20)))
- .andExpect(jsonPath("$.page.totalElements", is(1)));
-
+ getClient(tokenSubmitter).perform(get("/api/submission/workspaceitems/search/findBySubmitter")
+ .param("uuid", submitter.getID().toString()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.workspaceitems", Matchers.containsInAnyOrder(
+ WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(null, "Workflow Item 1",
+ "2017-10-17"))))
+ .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/submission/workspaceitems")))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", is(1)));
}
@Test
@@ -1564,6 +1564,153 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
;
}
+
+ @Test
+ public void findByItemUuidTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ //** GIVEN **
+ //1. A community-collection structure with one parent community with sub-community and two collections.
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
+ .withWorkflowGroup(1, admin).build();
+
+ //2. a workflow item
+ XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1)
+ .withTitle("Workflow Item 1")
+ .withIssueDate("2017-10-17")
+ .withAuthor("Smith, Donald").withAuthor("Doe, John")
+ .withSubject("ExtraEntry")
+ .build();
+
+ String authToken = getAuthToken(admin.getEmail(), password);
+
+ getClient(authToken).perform(get("/api/workflow/workflowitems/search/item")
+ .param("uuid", String.valueOf(witem.getItem().getID())))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",
+ Matchers.is(
+ WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
+ "Workflow Item 1", "2017-10-17", "ExtraEntry"))));
+
+ }
+
+ @Test
+ public void findByItemUuidMissingParameterTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ //** GIVEN **
+ //1. A community-collection structure with one parent community with sub-community and two collections.
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
+ .withWorkflowGroup(1, admin).build();
+
+
+ String token = getAuthToken(admin.getEmail(), password);
+ getClient(token).perform(get("/api/workflow/workflowitems/search/item"))
+ .andExpect(status().isBadRequest());
+ }
+
+ @Test
+ public void findByItemUuidDoesntExistTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ //** GIVEN **
+ //1. A community-collection structure with one parent community with sub-community and two collections.
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
+ .withWorkflowGroup(1, admin).build();
+
+
+ Item item = ItemBuilder.createItem(context, col1).build();
+ //2. a workspace item
+ XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1)
+ .withTitle("Workflow Item 1")
+ .withIssueDate("2017-10-17")
+ .withAuthor("Smith, Donald").withAuthor("Doe, John")
+ .withSubject("ExtraEntry")
+ .build();
+
+ String token = getAuthToken(admin.getEmail(), password);
+ getClient(token).perform(get("/api/workflow/workflowitems/search/item")
+ .param("uuid", String.valueOf(item.getID())))
+ .andExpect(status().isNoContent());
+ }
+
+ @Test
+ public void findByItemUuidForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+ context.setCurrentUser(admin);
+ //** GIVEN **
+ //1. A community-collection structure with one parent community with sub-community and two collections.
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
+ .withWorkflowGroup(1, admin).build();
+
+
+ XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1)
+ .withTitle("Workflow Item 1")
+ .withIssueDate("2017-10-17")
+ .withAuthor("Smith, Donald").withAuthor("Doe, John")
+ .withSubject("ExtraEntry")
+ .build();
+
+ context.restoreAuthSystemState();
+
+ String token = getAuthToken(eperson.getEmail(), password);
+ getClient(token).perform(get("/api/workflow/workflowitems/search/item")
+ .param("uuid", String.valueOf(witem.getItem().getID())))
+ .andExpect(status().isForbidden());
+ }
+
+ @Test
+ public void findByItemUuidUnAuthenticatedTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ //** GIVEN **
+ //1. A community-collection structure with one parent community with sub-community and two collections.
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
+ .withWorkflowGroup(1, admin).build();
+
+
+ XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1)
+ .withTitle("Workflow Item 1")
+ .withIssueDate("2017-10-17")
+ .withAuthor("Smith, Donald").withAuthor("Doe, John")
+ .withSubject("ExtraEntry")
+ .build();
+
+ getClient().perform(get("/api/workflow/workflowitems/search/item")
+ .param("uuid", String.valueOf(witem.getItem().getID())))
+ .andExpect(status().isUnauthorized());
+ }
+
@Test
public void stepEmbedTest() throws Exception {
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java
index d0b018b8b3..4d4cc72279 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java
@@ -28,11 +28,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
-
import javax.ws.rs.core.MediaType;
import com.fasterxml.jackson.databind.ObjectMapper;
-
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.CharEncoding;
import org.dspace.app.rest.builder.BitstreamBuilder;
@@ -40,6 +38,7 @@ import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.builder.EPersonBuilder;
import org.dspace.app.rest.builder.GroupBuilder;
+import org.dspace.app.rest.builder.ItemBuilder;
import org.dspace.app.rest.builder.WorkspaceItemBuilder;
import org.dspace.app.rest.matcher.CollectionMatcher;
import org.dspace.app.rest.matcher.ItemMatcher;
@@ -143,6 +142,8 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.withIssueDate("2016-02-13")
.build();
+ context.restoreAuthSystemState();
+
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/submission/workspaceitems"))
@@ -159,6 +160,92 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.andExpect(jsonPath("$.page.totalElements", is(3)));
}
+ @Test
+ public void findAllUnAuthenticatedTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+
+ Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
+ Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
+
+ WorkspaceItem workspaceItem1 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
+ .withTitle("Workspace Item 1")
+ .withIssueDate("2020-11-13")
+ .build();
+
+ WorkspaceItem workspaceItem2 = WorkspaceItemBuilder.createWorkspaceItem(context, col2)
+ .withTitle("Workspace Item 2")
+ .withIssueDate("2019-09-13")
+ .build();
+
+ context.restoreAuthSystemState();
+
+ getClient().perform(get("/api/submission/workspaceitems"))
+ .andExpect(status().isUnauthorized());
+ }
+
+ @Test
+ public void findAllForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ EPerson eperson1 = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson1@mail.com")
+ .withPassword("qwerty01")
+ .build();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+
+ Collection col1 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 1")
+ .build();
+
+ Collection col2 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 2")
+ .build();
+
+ context.setCurrentUser(eperson1);
+ WorkspaceItem workspaceItem1 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
+ .withTitle("Workspace Item 1")
+ .withIssueDate("2019-01-13")
+ .build();
+
+ WorkspaceItem workspaceItem2 = WorkspaceItemBuilder.createWorkspaceItem(context, col2)
+ .withTitle("Workspace Item 2")
+ .withIssueDate("2018-09-20")
+ .build();
+
+ context.restoreAuthSystemState();
+
+ String authTokenAdmin = getAuthToken(admin.getEmail(), password);
+ getClient(authTokenAdmin).perform(get("/api/submission/workspaceitems"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.workspaceitems", Matchers.containsInAnyOrder(
+ WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem1, "Workspace Item 1",
+ "2019-01-13"),
+ WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem2, "Workspace Item 2",
+ "2018-09-20"))))
+ .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/submission/workspaceitems")))
+ .andExpect(jsonPath("$.page.size", is(20)))
+ .andExpect(jsonPath("$.page.totalElements", is(2)));
+
+ String authToken = getAuthToken(eperson1.getEmail(), "qwerty01");
+ getClient(authToken).perform(get("/api/submission/workspaceitems"))
+ .andExpect(status().isForbidden());
+ }
+
@Test
/**
* The workspaceitem endpoint must provide proper pagination
@@ -196,6 +283,8 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.withIssueDate("2016-02-13")
.build();
+ context.restoreAuthSystemState();
+
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/submission/workspaceitems").param("size", "2"))
@@ -251,12 +340,63 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.withSubject("ExtraEntry")
.build();
- getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
+ context.restoreAuthSystemState();
+
+ String token = getAuthToken(eperson.getEmail(), password);
+ getClient(token).perform(get("/api/submission/workspaceitems/" + witem.getID())).andExpect(status().isOk())
.andExpect(jsonPath("$",
Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
"Workspace Item 1", "2017-10-17", "ExtraEntry"))));
}
+ @Test
+ public void findOneForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ EPerson eperson1 = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson1@mail.com")
+ .withPassword("qwerty01")
+ .build();
+
+ EPerson eperson2 = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson2@mail.com")
+ .withPassword("qwerty02")
+ .build();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+
+ Collection col1 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 1")
+ .build();
+
+ context.setCurrentUser(eperson1);
+ WorkspaceItem witem1 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
+ .withTitle("Workspace Item 1")
+ .withIssueDate("2019-09-09")
+ .withAuthor("Smith, Donald")
+ .withSubject("ExtraEntry")
+ .build();
+
+ context.restoreAuthSystemState();
+
+ String tokenEperson1 = getAuthToken(eperson1.getEmail(), "qwerty01");
+ getClient(tokenEperson1).perform(get("/api/submission/workspaceitems/" + witem1.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",
+ Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem1,
+ "Workspace Item 1", "2019-09-09", "ExtraEntry"))));
+
+ String tokenEperson2 = getAuthToken(eperson2.getEmail(), "qwerty02");
+ getClient(tokenEperson2).perform(get("/api/submission/workspaceitems/" + witem1.getID()))
+ .andExpect(status().isForbidden());
+ }
+
@Test
/**
* The workspaceitem resource endpoint must expose the proper structure
@@ -284,20 +424,22 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.withSubject("ExtraEntry")
.build();
- String token = getAuthToken(admin.getEmail(), password);
+ context.restoreAuthSystemState();
- getClient(token).perform(get("/api/submission/workspaceitems/" + witem.getID() + "/collection"))
+ String authToken = getAuthToken(admin.getEmail(), password);
+
+ getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID() + "/collection"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers
.is(CollectionMatcher.matchCollectionEntry(col1.getName(), col1.getID(), col1.getHandle()))
));
- getClient(token).perform(get("/api/submission/workspaceitems/" + witem.getID() + "/item"))
+ getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID() + "/item"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(ItemMatcher.matchItemWithTitleAndDateIssued(witem.getItem(),
"Workspace Item 1", "2017-10-17"))));
- getClient(token).perform(get("/api/submission/workspaceitems/" + witem.getID() + "/submissionDefinition"))
+ getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID() + "/submissionDefinition"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(hasJsonPath("$.id", is("traditional")))));
@@ -310,9 +452,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
* @throws Exception
*/
public void findOneWrongUUIDTest() throws Exception {
- String token = getAuthToken(admin.getEmail(), password);
+ String token = getAuthToken(eperson.getEmail(), password);
- getClient(token).perform(get("/api/submission/workspaceitems/" + UUID.randomUUID()))
+ getClient(token).perform(get("/api/submission/workspaceitems/" + Integer.MAX_VALUE))
.andExpect(status().isNotFound());
}
@@ -350,25 +492,137 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.withMimeType("text/plain").build();
}
- String token = getAuthToken(admin.getEmail(), password);
+ context.restoreAuthSystemState();
+
+ String token = getAuthToken(eperson.getEmail(), password);
//Delete the workspaceitem
getClient(token).perform(delete("/api/submission/workspaceitems/" + witem.getID()))
.andExpect(status().is(204));
//Trying to get deleted item should fail with 404
- getClient().perform(get("/api/submission/workspaceitems/" + witem.getID()))
+ getClient(token).perform(get("/api/submission/workspaceitems/" + witem.getID()))
.andExpect(status().is(404));
//Trying to get deleted workspaceitem's item should fail with 404
- getClient().perform(get("/api/core/items/" + item.getID()))
+ getClient(token).perform(get("/api/core/items/" + item.getID()))
.andExpect(status().is(404));
//Trying to get deleted workspaceitem's bitstream should fail with 404
- getClient().perform(get("/api/core/biststreams/" + bitstream.getID()))
+ getClient(token).perform(get("/api/core/biststreams/" + bitstream.getID()))
.andExpect(status().is(404));
}
+ @Test
+ public void deleteOneUnAuthenticatedTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Collection 1")
+ .build();
+ WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
+ .withTitle("Workspace Item 1")
+ .withIssueDate("2019-01-01")
+ .build();
+
+ Item item = witem.getItem();
+
+ //Add a bitstream to the item
+ String bitstreamContent = "ThisIsSomeDummyText";
+ Bitstream bitstream = null;
+ try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
+ bitstream = BitstreamBuilder
+ .createBitstream(context, item, is)
+ .withName("Bitstream1")
+ .withMimeType("text/plain").build();
+ }
+
+ context.restoreAuthSystemState();
+
+ getClient().perform(delete("/api/submission/workspaceitems/" + witem.getID()))
+ .andExpect(status().isUnauthorized());
+ }
+
+ @Test
+ public void deleteOneForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ EPerson submitter1 = EPersonBuilder.createEPerson(context)
+ .withEmail("submitter1@example.com")
+ .withPassword("qwerty01")
+ .build();
+ EPerson submitter2 = EPersonBuilder.createEPerson(context)
+ .withEmail("submitter2@example.com")
+ .withPassword("qwerty02")
+ .build();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
+ .withName("Collection 1")
+ .withSubmitterGroup(submitter1, submitter2)
+ .build();
+
+ context.setCurrentUser(submitter1);
+ WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
+ .withTitle("Workspace Item 1")
+ .withIssueDate("2020-01-01")
+ .build();
+
+ Item item = witem.getItem();
+
+ //Add a bitstream to the item
+ String bitstreamContent = "ThisIsSomeDummyText";
+ Bitstream bitstream = null;
+ try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
+ bitstream = BitstreamBuilder
+ .createBitstream(context, item, is)
+ .withName("Bitstream1")
+ .withMimeType("text/plain").build();
+ }
+
+ context.setCurrentUser(submitter2);
+ WorkspaceItem witem2 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
+ .withTitle("Workspace Item 2")
+ .withIssueDate("2020-02-02")
+ .build();
+
+ Item item2 = witem2.getItem();
+
+ String bitstreamContent2 = "ThisIsSomeDummyText2";
+ Bitstream bitstream2 = null;
+ try (InputStream is2 = IOUtils.toInputStream(bitstreamContent2, CharEncoding.UTF_8)) {
+ bitstream2 = BitstreamBuilder
+ .createBitstream(context, item2, is2)
+ .withName("Bitstream 2")
+ .withMimeType("text/plain").build();
+ }
+ context.restoreAuthSystemState();
+
+ // submitter2 attempt to delete the workspaceitem of submitter1
+ String tokenSubmitter2 = getAuthToken(submitter2.getEmail(), "qwerty02");
+ getClient(tokenSubmitter2).perform(delete("/api/submission/workspaceitems/" + witem.getID()))
+ .andExpect(status().isForbidden());
+
+ // check that workspaceitem was not deleted
+ String tokenAdmin = getAuthToken(admin.getEmail(), password);
+ getClient(tokenAdmin).perform(get("/api/submission/workspaceitems/" + witem.getID()))
+ .andExpect(status().isOk());
+
+ // a normal user, without any special submission rights, attempt to delete the workspaceitem of submitter1
+ String tokenEPerson = getAuthToken(eperson.getEmail(), password);
+ getClient(tokenEPerson).perform(delete("/api/submission/workspaceitems/" + witem.getID()))
+ .andExpect(status().isForbidden());
+
+ // check that workspaceitem was not deleted
+ getClient(tokenAdmin).perform(get("/api/submission/workspaceitems/" + witem.getID()))
+ .andExpect(status().isOk());
+ }
+
@Test
/**
* Create three workspaceitem with two different submitter and verify that the findBySubmitter return the proper
@@ -393,9 +647,11 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
//2. create two users to use as submitters
EPerson submitter1 = EPersonBuilder.createEPerson(context)
.withEmail("submitter1@example.com")
+ .withPassword("qwerty01")
.build();
EPerson submitter2 = EPersonBuilder.createEPerson(context)
.withEmail("submitter2@example.com")
+ .withPassword("qwerty02")
.build();
// create two workspaceitems with the first submitter
@@ -420,11 +676,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.withIssueDate("2016-02-13")
.build();
- // use our admin to retrieve all the workspace by submitter
- String token = getAuthToken(admin.getEmail(), password);
-
// the first submitter has two workspace
- getClient(token).perform(get("/api/submission/workspaceitems/search/findBySubmitter")
+ String tokenSubmitter1 = getAuthToken(submitter1.getEmail(), "qwerty01");
+ getClient(tokenSubmitter1).perform(get("/api/submission/workspaceitems/search/findBySubmitter")
.param("size", "20")
.param("uuid", submitter1.getID().toString()))
.andExpect(status().isOk())
@@ -442,7 +696,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// the first submitter has two workspace so if we paginate with a 1-size windows the page 1 will contains the
// second workspace
- getClient(token).perform(get("/api/submission/workspaceitems/search/findBySubmitter")
+ getClient(tokenSubmitter1).perform(get("/api/submission/workspaceitems/search/findBySubmitter")
.param("size", "1")
.param("page", "1")
.param("uuid", submitter1.getID().toString()))
@@ -460,7 +714,8 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.andExpect(jsonPath("$.page.totalElements", is(2)));
// the second submitter has a single workspace
- getClient(token).perform(get("/api/submission/workspaceitems/search/findBySubmitter")
+ String tokenSubmitter2 = getAuthToken(submitter2.getEmail(), "qwerty02");
+ getClient(tokenSubmitter2).perform(get("/api/submission/workspaceitems/search/findBySubmitter")
.param("size", "20")
.param("uuid", submitter2.getID().toString()))
.andExpect(status().isOk())
@@ -470,6 +725,72 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
"2016-02-13"))))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(1)));
+
+ // also the admin should be able to retrieve the submission of an another user
+ String tokenAdmin = getAuthToken(admin.getEmail(), password);
+ getClient(tokenAdmin).perform(get("/api/submission/workspaceitems/search/findBySubmitter")
+ .param("uuid", submitter1.getID().toString()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.workspaceitems", Matchers.containsInAnyOrder(
+ WorkspaceItemMatcher.matchItemWithTitleAndDateIssued
+ (workspaceItem1, "Workspace Item 1", "2017-10-17"),
+ WorkspaceItemMatcher.matchItemWithTitleAndDateIssued
+ (workspaceItem2, "Workspace Item 2", "2016-02-13"))))
+ .andExpect(jsonPath("$.page.totalElements", is(2)));
+ }
+
+ @Test
+ public void findBySubmitterForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+
+ Collection col1 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 1")
+ .build();
+
+ EPerson submitter1 = EPersonBuilder.createEPerson(context)
+ .withEmail("submitter1@example.com")
+ .withPassword("qwerty01")
+ .build();
+
+ EPerson submitter2 = EPersonBuilder.createEPerson(context)
+ .withEmail("submitter2@example.com")
+ .withPassword("qwerty02")
+ .build();
+
+ context.setCurrentUser(submitter1);
+
+ WorkspaceItem workspaceItem1 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
+ .withTitle("Workspace Item 1")
+ .withIssueDate("2019-10-17")
+ .build();
+
+ WorkspaceItem workspaceItem2 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
+ .withTitle("Workspace Item 2")
+ .withIssueDate("2018-02-13")
+ .build();
+
+ String tokenSubmitter1 = getAuthToken(submitter1.getEmail(), "qwerty01");
+ getClient(tokenSubmitter1).perform(get("/api/submission/workspaceitems/search/findBySubmitter")
+ .param("uuid", submitter1.getID().toString()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$._embedded.workspaceitems", Matchers.containsInAnyOrder(
+ WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem1, "Workspace Item 1",
+ "2019-10-17"),
+ WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(workspaceItem2, "Workspace Item 2",
+ "2018-02-13"))));
+
+ String tokenSubmitter2 = getAuthToken(submitter2.getEmail(), "qwerty02");
+ getClient(tokenSubmitter2).perform(get("/api/submission/workspaceitems/search/findBySubmitter")
+ .param("uuid", submitter1.getID().toString()))
+ .andExpect(status().isForbidden());
}
@Test
@@ -490,18 +811,26 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
- Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
- Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
- String authToken = getAuthToken(admin.getEmail(), password);
+ Collection col1 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 1")
+ .withSubmitterGroup(eperson)
+ .build();
+ Collection col2 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 2")
+ .withSubmitterGroup(eperson)
+ .build();
+ context.restoreAuthSystemState();
- // create a workspaceitem explicitly in the colAA1
+ String authToken = getAuthToken(eperson.getEmail(), password);
+
+ // create a workspaceitem explicitly in the col1
getClient(authToken).perform(post("/api/submission/workspaceitems")
.param("owningCollection", col1.getID().toString())
.contentType(org.springframework.http.MediaType.APPLICATION_JSON))
.andExpect(status().isCreated())
.andExpect(jsonPath("$._embedded.collection.id", is(col1.getID().toString())));
- // create a workspaceitem explicitly in the colAA2
+ // create a workspaceitem explicitly in the col2
getClient(authToken).perform(post("/api/submission/workspaceitems")
.param("owningCollection", col2.getID().toString())
.contentType(org.springframework.http.MediaType.APPLICATION_JSON))
@@ -536,16 +865,23 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
- Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
- Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
-
- String authToken = getAuthToken(admin.getEmail(), password);
+ Collection col1 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 1")
+ .withSubmitterGroup(eperson)
+ .build();
+ Collection col2 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 2")
+ .withSubmitterGroup(eperson)
+ .build();
InputStream bibtex = getClass().getResourceAsStream("bibtex-test.bib");
final MockMultipartFile bibtexFile = new MockMultipartFile("file", "bibtex-test.bib", "application/x-bibtex",
bibtex);
- // bulk create workspaceitems in the default collection (colAA1)
+ context.restoreAuthSystemState();
+
+ String authToken = getAuthToken(eperson.getEmail(), password);
+ // bulk create workspaceitems in the default collection (col1)
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
.file(bibtexFile))
// bulk create should return 200, 201 (created) is better for single resource
@@ -566,7 +902,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
jsonPath("$._embedded.workspaceitems[*]._embedded.upload").doesNotExist())
;
- // bulk create workspaceitems explicitly in the colAA2
+ // bulk create workspaceitems explicitly in the col2
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
.file(bibtexFile)
.param("collection", col2.getID().toString()))
@@ -608,13 +944,17 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
- Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
+ Collection col1 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 1")
+ .withSubmitterGroup(eperson)
+ .build();
- String authToken = getAuthToken(admin.getEmail(), password);
+ String authToken = getAuthToken(eperson.getEmail(), password);
InputStream pdf = getClass().getResourceAsStream("simple-article.pdf");
- final MockMultipartFile pdfFile = new MockMultipartFile("file", "/local/path/myfile.pdf", "application/pdf",
- pdf);
+ MockMultipartFile pdfFile = new MockMultipartFile("file", "/local/path/myfile.pdf", "application/pdf", pdf);
+
+ context.restoreAuthSystemState();
// bulk create a workspaceitem
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
@@ -655,26 +995,32 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
- Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
- String authToken = getAuthToken(admin.getEmail(), password);
+ Collection col1 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 1")
+ .withSubmitterGroup(eperson)
+ .build();
+
+ String authToken = getAuthToken(eperson.getEmail(), password);
WorkspaceItem workspaceItem1 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
.withTitle("Workspace Item 1")
.withIssueDate("2017-10-17")
.build();
+ WorkspaceItem workspaceItem2 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
+ .withTitle("Workspace Item 2")
+ .build();
+
//disable file upload mandatory
configurationService.setProperty("webui.submit.upload.required", false);
+ context.restoreAuthSystemState();
+
getClient(authToken).perform(get("/api/submission/workspaceitems/" + workspaceItem1.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
;
- WorkspaceItem workspaceItem2 = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
- .withTitle("Workspace Item 2")
- .build();
-
getClient(authToken).perform(get("/api/submission/workspaceitems/" + workspaceItem2.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors[?(@.message=='error.validation.required')]",
@@ -684,7 +1030,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
)))))
;
- // create an empty workspaceitem explicitly in the colAA1, check validation on creation
+ // create an empty workspaceitem explicitly in the col1, check validation on creation
getClient(authToken).perform(post("/api/submission/workspaceitems")
.param("collection", col1.getID().toString())
.contentType(org.springframework.http.MediaType.APPLICATION_JSON))
@@ -718,7 +1064,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
- String authToken = getAuthToken(admin.getEmail(), password);
+ String authToken = getAuthToken(eperson.getEmail(), password);
WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
.withTitle("Workspace Item 1")
@@ -729,6 +1075,8 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
//disable file upload mandatory
configurationService.setProperty("webui.submit.upload.required", false);
+ context.restoreAuthSystemState();
+
// a simple patch to update an existent metadata
List updateTitle = new ArrayList();
Map value = new HashMap();
@@ -746,10 +1094,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// check the new title and untouched values
Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
"New Title", "2017-10-17", "ExtraEntry"))));
- ;
// verify that the patch changes have been persisted
- getClient().perform(get("/api/submission/workspaceitems/" + witem.getID()))
+ getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$",
@@ -759,6 +1106,58 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
}
@Test
+ public void patchUpdateMetadataForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ EPerson eperson1 = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson1@mail.com")
+ .withPassword("qwerty01")
+ .build();
+
+ EPerson eperson2 = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson2@mail.com")
+ .withPassword("qwerty02")
+ .build();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 1")
+ .build();
+
+ context.setCurrentUser(eperson1);
+ WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
+ .withTitle("Workspace Item 1")
+ .withIssueDate("2019-01-01")
+ .withSubject("ExtraEntry")
+ .build();
+
+ context.restoreAuthSystemState();
+
+ List updateIssueDate = new ArrayList();
+ Map value = new HashMap();
+ value.put("value", "2020-01-01");
+ updateIssueDate.add(new ReplaceOperation("/sections/traditionalpageone/dc.date.issued/0", value));
+
+ String patchBody = getPatchContent(updateIssueDate);
+ String tokenEperson2 = getAuthToken(eperson2.getEmail(), "qwerty02");
+ getClient(tokenEperson2).perform(patch("/api/submission/workspaceitems/" + witem.getID())
+ .content(patchBody)
+ .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
+ .andExpect(status().isForbidden());
+
+ String tokenEperson1 = getAuthToken(eperson1.getEmail(), "qwerty01");
+ getClient(tokenEperson1).perform(get("/api/submission/workspaceitems/" + witem.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",
+ Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject
+ (witem, "Workspace Item 1", "2019-01-01", "ExtraEntry"))));
+ }
+
public void patchReplaceMetadataOnItemStillInSubmissionTest() throws Exception {
context.turnOffAuthorisationSystem();
@@ -848,6 +1247,53 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.andExpect(jsonPath("$.metadata.['dc.date.issued']").doesNotExist());
}
+ @Test
+ public void patchUpdateMetadataUnAuthenticatedTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ EPerson eperson1 = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson1@mail.com")
+ .withPassword("qwerty01")
+ .build();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 1")
+ .build();
+
+ context.setCurrentUser(eperson1);
+ WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
+ .withTitle("Workspace Item 1")
+ .withIssueDate("2019-01-01")
+ .withSubject("ExtraEntry")
+ .build();
+
+ context.restoreAuthSystemState();
+
+ List updateIssueDate = new ArrayList();
+ Map value = new HashMap();
+ value.put("value", "2020-01-01");
+ updateIssueDate.add(new ReplaceOperation("/sections/traditionalpageone/dc.date.issued/0", value));
+
+ String patchBody = getPatchContent(updateIssueDate);
+ getClient().perform(patch("/api/submission/workspaceitems/" + witem.getID())
+ .content(patchBody)
+ .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
+ .andExpect(status().isUnauthorized());
+
+ String tokenEperson1 = getAuthToken(eperson1.getEmail(), "qwerty01");
+ getClient(tokenEperson1).perform(get("/api/submission/workspaceitems/" + witem.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",
+ Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject
+ (witem, "Workspace Item 1", "2019-01-01", "ExtraEntry"))));
+ }
+
@Test
public void patchRemoveMetadataOnItemStillInSubmissionTest() throws Exception {
context.turnOffAuthorisationSystem();
@@ -910,7 +1356,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
- String authToken = getAuthToken(admin.getEmail(), password);
+ String authToken = getAuthToken(eperson.getEmail(), password);
WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
.withTitle("Workspace Item 1")
@@ -936,6 +1382,8 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.withSubject("Subject4")
.build();
+ context.restoreAuthSystemState();
+
// try to remove the title
List removeTitle = new ArrayList();
removeTitle.add(new RemoveOperation("/sections/traditionalpageone/dc.title/0"));
@@ -954,10 +1402,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// check the new title and untouched values
Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
null, "2017-10-17", "ExtraEntry"))));
- ;
// verify that the patch changes have been persisted
- getClient().perform(get("/api/submission/workspaceitems/" + witem.getID()))
+ getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors[?(@.message=='error.validation.required')]",
Matchers.contains(
@@ -988,7 +1435,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
;
// verify that the patch changes have been persisted
- getClient().perform(get("/api/submission/workspaceitems/" + witemMultipleSubjects.getID()))
+ getClient(authToken).perform(get("/api/submission/workspaceitems/" + witemMultipleSubjects.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject1")))
@@ -1010,7 +1457,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
;
// verify that the patch changes have been persisted
- getClient().perform(get("/api/submission/workspaceitems/" + witemMultipleSubjects.getID()))
+ getClient(authToken).perform(get("/api/submission/workspaceitems/" + witemMultipleSubjects.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject3")))
@@ -1030,7 +1477,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
;
// verify that the patch changes have been persisted
- getClient().perform(get("/api/submission/workspaceitems/" + witemMultipleSubjects.getID()))
+ getClient(authToken).perform(get("/api/submission/workspaceitems/" + witemMultipleSubjects.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject'][0].value", is("Subject3")))
@@ -1049,7 +1496,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
;
// verify that the patch changes have been persisted
- getClient().perform(get("/api/submission/workspaceitems/" + witemMultipleSubjects.getID()))
+ getClient(authToken).perform(get("/api/submission/workspaceitems/" + witemMultipleSubjects.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject']").doesNotExist())
@@ -1069,13 +1516,109 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
;
// verify that the patch changes have been persisted
- getClient().perform(get("/api/submission/workspaceitems/" + witemWithTitleDateAndSubjects.getID()))
+ getClient(authToken).perform(get("/api/submission/workspaceitems/" + witemWithTitleDateAndSubjects.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject']").doesNotExist())
;
}
+ @Test
+ public void patchDeleteMetadataForbiddenTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ EPerson eperson1 = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson1@mail.com")
+ .withPassword("qwerty01")
+ .build();
+
+ EPerson eperson2 = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson2@mail.com")
+ .withPassword("qwerty02")
+ .build();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 1")
+ .build();
+
+ context.setCurrentUser(eperson1);
+ WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
+ .withTitle("Workspace Item 1")
+ .withIssueDate("2019-01-01")
+ .withSubject("ExtraEntry")
+ .build();
+
+ context.restoreAuthSystemState();
+
+ List deleteIssueDate = new ArrayList();
+ deleteIssueDate.add(new RemoveOperation("/sections/traditionalpageone/dc.date.issued/0"));
+
+ String patchBody = getPatchContent(deleteIssueDate);
+ String tokenEperson2 = getAuthToken(eperson2.getEmail(), "qwerty02");
+ getClient(tokenEperson2).perform(patch("/api/submission/workspaceitems/" + witem.getID())
+ .content(patchBody)
+ .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
+ .andExpect(status().isForbidden());
+
+ String tokenEperson1 = getAuthToken(eperson1.getEmail(), "qwerty01");
+ getClient(tokenEperson1).perform(get("/api/submission/workspaceitems/" + witem.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",
+ Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject
+ (witem, "Workspace Item 1", "2019-01-01", "ExtraEntry"))));
+ }
+
+ @Test
+ public void patchDeleteMetadataUnAuthenticatedTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ EPerson eperson1 = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson1@mail.com")
+ .withPassword("qwerty01")
+ .build();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, child1)
+ .withName("Collection 1")
+ .build();
+
+ context.setCurrentUser(eperson1);
+ WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
+ .withTitle("Workspace Item 1")
+ .withIssueDate("2019-01-01")
+ .withSubject("ExtraEntry")
+ .build();
+
+ context.restoreAuthSystemState();
+
+ List deleteIssueDate = new ArrayList();
+ deleteIssueDate.add(new RemoveOperation("/sections/traditionalpageone/dc.date.issued/0"));
+
+ String patchBody = getPatchContent(deleteIssueDate);
+ getClient().perform(patch("/api/submission/workspaceitems/" + witem.getID())
+ .content(patchBody)
+ .contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
+ .andExpect(status().isUnauthorized());
+
+ String tokenEperson1 = getAuthToken(eperson1.getEmail(), "qwerty01");
+ getClient(tokenEperson1).perform(get("/api/submission/workspaceitems/" + witem.getID()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$",
+ Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject
+ (witem, "Workspace Item 1", "2019-01-01", "ExtraEntry"))));
+ }
+
@Test
/**
* Test the addition of metadata
@@ -1094,7 +1637,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
- String authToken = getAuthToken(admin.getEmail(), password);
+ String authToken = getAuthToken(eperson.getEmail(), password);
WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
.withIssueDate("2017-10-17")
@@ -1104,6 +1647,8 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
//disable file upload mandatory
configurationService.setProperty("webui.submit.upload.required", false);
+ context.restoreAuthSystemState();
+
// try to add the title
List addTitle = new ArrayList();
// create a list of values to use in add operation
@@ -1123,10 +1668,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// check if the new title if back and the other values untouched
Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
"New Title", "2017-10-17", "ExtraEntry"))));
- ;
// verify that the patch changes have been persisted
- getClient().perform(get("/api/submission/workspaceitems/" + witem.getID()))
+ getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$",
@@ -1135,6 +1679,146 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
;
}
+ @Test
+ public void patchAddMetadataUpdateExistValueTest() throws Exception {
+ context.turnOffAuthorisationSystem();
+
+ EPerson eperson1 = EPersonBuilder.createEPerson(context)
+ .withEmail("eperson1@mail.com")
+ .withPassword("qwerty01")
+ .build();
+
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+ Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .build();
+ Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
+
+ context.setCurrentUser(eperson1);
+ WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
+ .withTitle("Workspace Item 1")
+ .withIssueDate("2017-10-17")
+ .withSubject("ExtraEntry")
+ .build();
+
+ context.restoreAuthSystemState();
+
+ List addTitle = new ArrayList();
+ List