Merge remote-tracking branch 'community/master' into w2p-68820_Moving-relationships-ITs

This commit is contained in:
Ben Bosman
2020-04-01 16:58:29 +02:00
160 changed files with 9748 additions and 576 deletions

30
.github/pull_request_template.md vendored Normal file
View File

@@ -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.

11
LICENSE
View File

@@ -1,7 +1,6 @@
DSpace source code license: DSpace source code BSD License:
Copyright (c) 2002-2020, LYRASIS. All rights reserved.
Copyright (c) 2002-2016, DuraSpace. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are modification, are permitted provided that the following conditions are
@@ -34,7 +33,7 @@ DAMAGE.
DSpace uses third-party libraries which may be distributed under DSpace uses third-party libraries which may be distributed under
different licenses to the above. Information about these licenses different licenses to the above. Information about these licenses
is detailed in the LICENSES_THIRD_PARTY file at the root of the source 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 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. the above DSpace source code license, in order to use this software.

21
NOTICE
View File

@@ -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 new name DuraSpace in July 2009. DuraSpace holds the copyrights of
the DSpace Foundation, Inc. the DSpace Foundation, Inc.
The DSpace Foundation, Inc. is a 501(c)3 corporation established in July 2007 [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, with a mission to promote and advance the dspace platform enabling management,
access and preservation of digital works. The Foundation was able to transfer access and preservation of digital works. The Foundation was able to transfer
the legal copyright from Hewlett-Packard Company (HP) and Massachusetts the legal copyright from Hewlett-Packard Company (HP) and Massachusetts
Institute of Technology (MIT) to the DSpace Foundation in October 2007. Many 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 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 and MIT possess the copyright, in these instances please note that the copy
right has transferred to the DSpace foundation, and subsequently to DuraSpace. right has transferred to the DSpace foundation, and subsequently to DuraSpace.

View File

@@ -44,15 +44,16 @@ For more information on CheckStyle configurations below, see: http://checkstyle.
with @SuppressWarnings. See also SuppressWarningsHolder below --> with @SuppressWarnings. See also SuppressWarningsHolder below -->
<module name="SuppressWarningsFilter" /> <module name="SuppressWarningsFilter" />
<!-- Maximum line length is 120 characters -->
<module name="LineLength">
<property name="fileExtensions" value="java"/>
<property name="max" value="120"/>
<!-- Only exceptions for packages, imports, URLs, and JavaDoc {@link} tags -->
<property name="ignorePattern" value="^package.*|^import.*|http://|https://|@link"/>
</module>
<!-- Check individual Java source files for specific rules --> <!-- Check individual Java source files for specific rules -->
<module name="TreeWalker"> <module name="TreeWalker">
<!-- Maximum line length is 120 characters -->
<module name="LineLength">
<property name="max" value="120"/>
<!-- Only exceptions for packages, imports, URLs, and JavaDoc {@link} tags -->
<property name="ignorePattern" value="^package.*|^import.*|http://|https://|@link"/>
</module>
<!-- Highlight any TODO or FIXME comments in info messages --> <!-- Highlight any TODO or FIXME comments in info messages -->
<module name="TodoComment"> <module name="TodoComment">
<property name="severity" value="info"/> <property name="severity" value="info"/>
@@ -94,11 +95,8 @@ For more information on CheckStyle configurations below, see: http://checkstyle.
<!-- <property name="scope" value="public"/> --> <!-- <property name="scope" value="public"/> -->
<!-- TODO: Above rule has been disabled because of large amount of missing public method Javadocs --> <!-- TODO: Above rule has been disabled because of large amount of missing public method Javadocs -->
<property name="scope" value="nothing"/> <property name="scope" value="nothing"/>
<!-- Allow RuntimeExceptions to be undeclared -->
<property name="allowUndeclaredRTE" value="true"/>
<!-- Allow params, throws and return tags to be optional --> <!-- Allow params, throws and return tags to be optional -->
<property name="allowMissingParamTags" value="true"/> <property name="allowMissingParamTags" value="true"/>
<property name="allowMissingThrowsTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/> <property name="allowMissingReturnTag" value="true"/>
</module> </module>

View File

@@ -190,10 +190,11 @@ public class AuthorizeUtil {
public static void authorizeManageCCLicense(Context context, Item item) public static void authorizeManageCCLicense(Context context, Item item)
throws AuthorizeException, SQLException { throws AuthorizeException, SQLException {
AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
ItemService itemService = ContentServiceFactory.getInstance().getItemService(); ItemService itemService = ContentServiceFactory.getInstance().getItemService();
try { try {
authorizeService.authorizeAction(context, item, Constants.ADD); authorizeService.authorizeAction(context, item, Constants.ADD, false);
authorizeService.authorizeAction(context, item, Constants.REMOVE); authorizeService.authorizeAction(context, item, Constants.REMOVE, false);
} catch (AuthorizeException authex) { } catch (AuthorizeException authex) {
if (AuthorizeConfiguration.canItemAdminManageCCLicense()) { if (AuthorizeConfiguration.canItemAdminManageCCLicense()) {
authorizeService authorizeService
@@ -202,8 +203,10 @@ public class AuthorizeUtil {
authorizeService.authorizeAction(context, itemService authorizeService.authorizeAction(context, itemService
.getParentObject(context, item), Constants.ADMIN); .getParentObject(context, item), Constants.ADMIN);
} else if (AuthorizeConfiguration.canCommunityAdminManageCCLicense()) { } else if (AuthorizeConfiguration.canCommunityAdminManageCCLicense()) {
authorizeService.authorizeAction(context, itemService Collection collection = (Collection) itemService
.getParentObject(context, item), Constants.ADMIN); .getParentObject(context, item);
authorizeService.authorizeAction(context, collectionService.getParentObject(context, collection),
Constants.ADMIN);
} else { } else {
requireAdminRole(context); requireAdminRole(context);
} }

View File

@@ -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.entity.OSQuery;
import com.sun.syndication.feed.module.opensearch.impl.OpenSearchModuleImpl; import com.sun.syndication.feed.module.opensearch.impl.OpenSearchModuleImpl;
import com.sun.syndication.io.FeedException; import com.sun.syndication.io.FeedException;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.dspace.app.util.service.OpenSearchService; import org.dspace.app.util.service.OpenSearchService;
import org.dspace.content.DSpaceObject; import org.dspace.content.DSpaceObject;

View File

@@ -34,6 +34,7 @@ import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.MetadataFieldService; import org.dspace.content.service.MetadataFieldService;
import org.dspace.content.service.MetadataSchemaService; import org.dspace.content.service.MetadataSchemaService;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.core.Utils;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group; import org.dspace.eperson.Group;
import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.factory.EPersonServiceFactory;
@@ -492,35 +493,22 @@ public class ShibAuthentication implements AuthenticationMethod {
boolean lazySession = configurationService.getBooleanProperty("authentication-shibboleth.lazysession", false); boolean lazySession = configurationService.getBooleanProperty("authentication-shibboleth.lazysession", false);
if ( lazySession ) { if ( lazySession ) {
String shibURL = configurationService.getProperty("authentication-shibboleth.lazysession.loginurl"); String shibURL = getShibURL(request);
boolean forceHTTPS =
configurationService.getBooleanProperty("authentication-shibboleth.lazysession.secure",true);
// Shibboleth authentication initiator // Determine the client redirect URL, where to redirect after authenticating.
if (shibURL == null || shibURL.length() == 0) { String redirectUrl = null;
shibURL = "/Shibboleth.sso/Login"; 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 // Determine the server return URL, where shib will send the user after authenticating.
// to DSpace's shibboleth-login url so the we will extract the user's information and locally // We need it to go back to DSpace's shibboleth-login url so we will extract the user's information
// authenticate them. // and locally authenticate them.
String host = request.getServerName(); String returnURL = configurationService.getProperty("dspace.server.url") + "/api/authn/shibboleth"
int port = request.getServerPort(); + ((redirectUrl != null) ? "?redirectUrl=" + redirectUrl : "");
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;
}
}
try { try {
shibURL += "?target=" + URLEncoder.encode(returnURL, "UTF-8"); shibURL += "?target=" + URLEncoder.encode(returnURL, "UTF-8");
@@ -1258,6 +1246,23 @@ public class ShibAuthentication implements AuthenticationMethod {
return valueList; 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;
}
} }

View File

@@ -7,7 +7,8 @@
*/ */
package org.dspace.authorize; 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 * This class is responsible to provide access to the configuration of the
@@ -16,164 +17,26 @@ import org.dspace.core.ConfigurationManager;
* @author bollini * @author bollini
*/ */
public class AuthorizeConfiguration { public class AuthorizeConfiguration {
/**
private static boolean can_communityAdmin_group = ConfigurationManager * A static reference to the {@link ConfigurationService} see the init method for initialization
.getBooleanProperty("core.authorization.community-admin.group", */
true); private static ConfigurationService configurationService;
// 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);
/** /**
* Default constructor * Default constructor
*/ */
private AuthorizeConfiguration() { } 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 * Are community admins allowed to create new, not strictly community
* related, group? * related, group?
@@ -181,7 +44,8 @@ public class AuthorizeConfiguration {
* @return true/false * @return true/false
*/ */
public static boolean canCommunityAdminPerformGroupCreation() { 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 * @return true/false
*/ */
public static boolean canCommunityAdminPerformSubelementCreation() { 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 * @return true/false
*/ */
public static boolean canCommunityAdminPerformSubelementDeletion() { 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 * @return true/false
*/ */
public static boolean canCommunityAdminManagePolicies() { 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 * @return true/false
*/ */
public static boolean canCommunityAdminManageAdminGroup() { 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 * @return true/false
*/ */
public static boolean canCommunityAdminManageCollectionPolicies() { 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 * @return true/false
*/ */
public static boolean canCommunityAdminManageCollectionTemplateItem() { 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 * @return true/false
*/ */
public static boolean canCommunityAdminManageCollectionSubmitters() { 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 * @return true/false
*/ */
public static boolean canCommunityAdminManageCollectionWorkflows() { 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 * @return true/false
*/ */
public static boolean canCommunityAdminManageCollectionAdminGroup() { 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 * @return true/false
*/ */
public static boolean canCommunityAdminPerformItemDeletion() { 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 * @return true/false
*/ */
public static boolean canCommunityAdminPerformItemWithdrawn() { 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 * @return true/false
*/ */
public static boolean canCommunityAdminPerformItemReinstatiate() { 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 * @return true/false
*/ */
public static boolean canCommunityAdminManageItemPolicies() { 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 * @return true/false
*/ */
public static boolean canCommunityAdminPerformBitstreamCreation() { 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 * @return true/false
*/ */
public static boolean canCommunityAdminPerformBitstreamDeletion() { 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 * @return true/false
*/ */
public static boolean canCommunityAdminManageCCLicense() { 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 * @return true/false
*/ */
public static boolean canCollectionAdminManagePolicies() { 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 * @return true/false
*/ */
public static boolean canCollectionAdminManageTemplateItem() { 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 * @return true/false
*/ */
public static boolean canCollectionAdminManageSubmitters() { 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 * @return true/false
*/ */
public static boolean canCollectionAdminManageWorkflows() { 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 * @return true/false
*/ */
public static boolean canCollectionAdminManageAdminGroup() { 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 * @return true/false
*/ */
public static boolean canCollectionAdminPerformItemDeletion() { 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 * @return true/false
*/ */
public static boolean canCollectionAdminPerformItemWithdrawn() { 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 * @return true/false
*/ */
public static boolean canCollectionAdminPerformItemReinstatiate() { 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 * @return true/false
*/ */
public static boolean canCollectionAdminManageItemPolicies() { 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 * @return true/false
*/ */
public static boolean canCollectionAdminPerformBitstreamCreation() { 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 * @return true/false
*/ */
public static boolean canCollectionAdminPerformBitstreamDeletion() { 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 * @return true/false
*/ */
public static boolean canCollectionAdminManageCCLicense() { 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 * @return true/false
*/ */
public static boolean canItemAdminManagePolicies() { 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 * @return true/false
*/ */
public static boolean canItemAdminPerformBitstreamCreation() { 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 * @return true/false
*/ */
public static boolean canItemAdminPerformBitstreamDeletion() { 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 * @return true/false
*/ */
public static boolean canItemAdminManageCCLicense() { public static boolean canItemAdminManageCCLicense() {
return can_itemAdmin_ccLicense; init();
return configurationService.getBooleanProperty("core.authorization.item-admin.cc-license", true);
} }
} }

View File

@@ -8,10 +8,8 @@
package org.dspace.authorize.dao.impl; package org.dspace.authorize.dao.impl;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.persistence.Query; import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.CriteriaQuery;

View File

@@ -11,7 +11,6 @@ import java.io.InputStream;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType; import javax.persistence.FetchType;

View File

@@ -13,7 +13,6 @@ import java.sql.SQLException;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;

View File

@@ -12,7 +12,6 @@ import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.persistence.Cacheable; import javax.persistence.Cacheable;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
import javax.persistence.Column; import javax.persistence.Column;

View File

@@ -11,7 +11,6 @@ import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.persistence.Cacheable; import javax.persistence.Cacheable;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
import javax.persistence.Column; import javax.persistence.Column;
@@ -271,4 +270,4 @@ public class Community extends DSpaceObject implements DSpaceObjectLegacySupport
return communityService; return communityService;
} }
} }

View File

@@ -11,7 +11,6 @@ import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;

View File

@@ -13,7 +13,6 @@ import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;

View File

@@ -10,7 +10,6 @@ package org.dspace.content;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType; import javax.persistence.FetchType;

View File

@@ -8,7 +8,6 @@
package org.dspace.content.dao.impl; package org.dspace.content.dao.impl;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.CriteriaQuery;
@@ -21,7 +20,7 @@ import org.dspace.scripts.Process;
import org.dspace.scripts.Process_; import org.dspace.scripts.Process_;
/** /**
* *
* Implementation class for {@link ProcessDAO} * Implementation class for {@link ProcessDAO}
*/ */
public class ProcessDAOImpl extends AbstractHibernateDAO<Process> implements ProcessDAO { public class ProcessDAOImpl extends AbstractHibernateDAO<Process> implements ProcessDAO {

View File

@@ -13,7 +13,6 @@ import java.sql.SQLException;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.AuthorizeException;
@@ -46,7 +45,7 @@ public interface BitstreamService extends DSpaceObjectService<Bitstream>, DSpace
* checksum algorithm as same as the given bitstream. * checksum algorithm as same as the given bitstream.
* This allows multiple bitstreams to share the same internal identifier of assets . * This allows multiple bitstreams to share the same internal identifier of assets .
* An example of such a use case scenario is versioning. * An example of such a use case scenario is versioning.
* *
* @param context * @param context
* DSpace context object * DSpace context object
* @param bitstream * @param bitstream

View File

@@ -58,6 +58,11 @@ public class Context implements AutoCloseable {
*/ */
private EPerson currentUser; private EPerson currentUser;
/**
* Temporary store when the current user is temporary switched
*/
private EPerson currentUserPreviousState;
/** /**
* Current Locale * Current Locale
*/ */
@@ -89,6 +94,11 @@ public class Context implements AutoCloseable {
*/ */
private List<UUID> specialGroups; private List<UUID> specialGroups;
/**
* Temporary store for the specialGroups when the current user is temporary switched
*/
private List<UUID> specialGroupsPreviousState;
/** /**
* Content events * Content events
*/ */
@@ -628,6 +638,42 @@ public class Context implements AutoCloseable {
return myGroups; 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<UUID>();
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). * Close the context, aborting any open transactions (if any).
* @throws Throwable * @throws Throwable

View File

@@ -13,8 +13,10 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.math.BigInteger; import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL;
import java.rmi.dgc.VMID; import java.rmi.dgc.VMID;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; 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 * Retrieve the hostname from a given URI string

View File

@@ -21,7 +21,7 @@ import org.dspace.core.ReloadableEntity;
* @param <PK> * @param <PK>
* the Class of the primary key * the Class of the primary key
*/ */
public interface IndexableObject<T extends ReloadableEntity, PK extends Serializable> { public interface IndexableObject<T extends ReloadableEntity<PK>, PK extends Serializable> {
/** /**
* *

View File

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

View File

@@ -18,7 +18,11 @@ import org.apache.solr.common.SolrInputDocument;
import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService; import org.dspace.authorize.service.AuthorizeService;
import org.dspace.authorize.service.ResourcePolicyService; 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.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService; import org.dspace.content.service.CollectionService;
import org.dspace.content.service.CommunityService; import org.dspace.content.service.CommunityService;
import org.dspace.core.Constants; import org.dspace.core.Constants;
@@ -77,6 +81,29 @@ public class SolrServiceResourceRestrictionPlugin implements SolrServiceIndexPlu
//remove the policy from the cache to save memory //remove the policy from the cache to save memory
context.uncacheEntity(resourcePolicy); 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<ResourcePolicy> 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) { } catch (SQLException e) {
log.error(LogManager.getHeader(context, "Error while indexing resource policies", log.error(LogManager.getHeader(context, "Error while indexing resource policies",
"DSpace object: (id " + dso.getID() + " type " + dso.getType() + ")")); "DSpace object: (id " + dso.getID() + " type " + dso.getType() + ")"));

View File

@@ -21,7 +21,6 @@ import net.handle.hdllib.HandleStorage;
import net.handle.hdllib.HandleValue; import net.handle.hdllib.HandleValue;
import net.handle.hdllib.ScanCallback; import net.handle.hdllib.ScanCallback;
import net.handle.hdllib.Util; import net.handle.hdllib.Util;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.handle.factory.HandleServiceFactory;

View File

@@ -15,7 +15,6 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.apache.commons.collections4.MapUtils; import org.apache.commons.collections4.MapUtils;

View File

@@ -18,7 +18,6 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.MissingResourceException; import java.util.MissingResourceException;
import java.util.UUID; import java.util.UUID;
import javax.mail.MessagingException; import javax.mail.MessagingException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;

View File

@@ -11,7 +11,6 @@ import java.io.IOException;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.dspace.app.util.Util; import org.dspace.app.util.Util;

View File

@@ -9,7 +9,6 @@ package org.dspace.xmlworkflow.storedcomponents.dao.impl;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join; import javax.persistence.criteria.Join;

View File

@@ -7,6 +7,17 @@
*/ */
package org.dspace; 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; import org.junit.Ignore;
/** /**
@@ -21,5 +32,102 @@ import org.junit.Ignore;
*/ */
@Ignore @Ignore
public class AbstractIntegrationTest extends AbstractUnitTest { 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 <code>dspace/config/config-definition.xml</code>
*/
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);
}
}
} }

View File

@@ -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());
}
}

View File

@@ -22,6 +22,33 @@ import org.junit.Test;
*/ */
public class UtilsTest extends AbstractUnitTest { 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 * Test of getHostName method, of class Utils
*/ */

View File

@@ -13,7 +13,6 @@ import static org.junit.Assert.fail;
import java.sql.SQLException; import java.sql.SQLException;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.dspace.AbstractUnitTest; import org.dspace.AbstractUnitTest;
import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Collection; import org.dspace.content.Collection;
@@ -24,7 +23,6 @@ import org.dspace.content.service.CommunityService;
import org.dspace.utils.DSpace; import org.dspace.utils.DSpace;
import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; import org.dspace.xmlworkflow.factory.XmlWorkflowFactory;
import org.dspace.xmlworkflow.state.Workflow; import org.dspace.xmlworkflow.state.Workflow;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;

View File

@@ -22,7 +22,6 @@ import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamException;
import com.lyncode.xoai.dataprovider.exceptions.ConfigurationException; import com.lyncode.xoai.dataprovider.exceptions.ConfigurationException;

View File

@@ -9,7 +9,6 @@ package org.dspace.xoai.services.impl;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Date; import java.util.Date;
import javax.persistence.NoResultException; import javax.persistence.NoResultException;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;

View File

@@ -10,6 +10,7 @@ package org.dspace.app.rest;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays; import java.util.Arrays;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.dspace.app.rest.converter.ConverterService; import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.converter.EPersonConverter; 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.AuthenticationStatusResource;
import org.dspace.app.rest.model.hateoas.AuthnResource; import org.dspace.app.rest.model.hateoas.AuthnResource;
import org.dspace.app.rest.projection.Projection; 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.ContextUtil;
import org.dspace.app.rest.utils.Utils; import org.dspace.app.rest.utils.Utils;
import org.dspace.core.Context; import org.dspace.core.Context;
@@ -60,6 +62,9 @@ public class AuthenticationRestController implements InitializingBean {
@Autowired @Autowired
private HalLinkService halLinkService; private HalLinkService halLinkService;
@Autowired
private RestAuthenticationService restAuthenticationService;
@Autowired @Autowired
private Utils utils; private Utils utils;
@@ -77,7 +82,8 @@ public class AuthenticationRestController implements InitializingBean {
} }
@RequestMapping(value = "/status", method = RequestMethod.GET) @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); Context context = ContextUtil.obtainContext(request);
EPersonRest ePersonRest = null; EPersonRest ePersonRest = null;
Projection projection = utils.obtainProjection(); Projection projection = utils.obtainProjection();
@@ -86,6 +92,14 @@ public class AuthenticationRestController implements InitializingBean {
} }
AuthenticationStatusRest authenticationStatusRest = new AuthenticationStatusRest(ePersonRest); 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); authenticationStatusRest.setProjection(projection);
AuthenticationStatusResource authenticationStatusResource = converter.toResource(authenticationStatusRest); AuthenticationStatusResource authenticationStatusResource = converter.toResource(authenticationStatusRest);

View File

@@ -10,7 +10,6 @@ package org.dspace.app.rest;
import java.io.IOException; import java.io.IOException;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.UUID; import java.util.UUID;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.exception.UnprocessableEntityException;

View File

@@ -12,7 +12,6 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;

View File

@@ -36,6 +36,7 @@ import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.converter.JsonPatchConverter; import org.dspace.app.rest.converter.JsonPatchConverter;
import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.exception.PaginationException; 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.RepositoryMethodNotImplementedException;
import org.dspace.app.rest.exception.RepositoryNotFoundException; import org.dspace.app.rest.exception.RepositoryNotFoundException;
import org.dspace.app.rest.exception.RepositorySearchMethodNotFoundException; import org.dspace.app.rest.exception.RepositorySearchMethodNotFoundException;
@@ -583,9 +584,11 @@ public class RestResourceController implements InitializingBean {
RestAddressableModel modelObject = null; RestAddressableModel modelObject = null;
try { try {
modelObject = repository.upload(request, apiCategory, model, id, uploadfile); modelObject = repository.upload(request, apiCategory, model, id, uploadfile);
} catch (SQLException e) { } catch (SQLException | IOException e) {
log.error(e.getMessage(), e); throw new RuntimeException("Error " + e.getMessage() +
return ControllerUtils.toEmptyResponse(HttpStatus.INTERNAL_SERVER_ERROR); " uploading file to " + model + " with ID= " + id, e);
} catch ( AuthorizeException ae) {
throw new RESTAuthorizationException(ae);
} }
DSpaceResource result = converter.toResource(modelObject); DSpaceResource result = converter.toResource(modelObject);
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, new HttpHeaders(), result); return ControllerUtils.toResponseEntity(HttpStatus.CREATED, new HttpHeaders(), result);

View File

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

View File

@@ -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. <code>null</code> 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. <code>null</code> mean unauthenticated user
*/
public EPerson getEperson() {
return eperson;
}
/**
*
* @param eperson
* the user authorized to use the feature. <code>null</code> 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();
}
}

View File

@@ -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();
}

View File

@@ -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 "";
}

View File

@@ -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<AuthorizationFeature> 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<AuthorizationFeature> findByResourceType(String categoryDotModel);
}

View File

@@ -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 };
}
}

View File

@@ -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<AuthorizationFeature> 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<AuthorizationFeature> 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<AuthorizationFeature> 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());
}
}

View File

@@ -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 };
}
}

View File

@@ -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 };
}
}

View File

@@ -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 };
}
}

View File

@@ -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<Authorization, AuthorizationRest> {
@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<Authorization> getModelClass() {
return Authorization.class;
}
}

View File

@@ -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<AuthorizationFeature, AuthorizationFeatureRest> {
@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<String> types = new ArrayList<String>();
for (String t : feature.getSupportedTypes()) {
types.add(t);
}
featureRest.setResourceTypes(types);
}
return featureRest;
}
@Override
public Class<AuthorizationFeature> getModelClass() {
return AuthorizationFeature.class;
}
}

View File

@@ -38,7 +38,9 @@ public class ScriptConverter implements DSpaceConverter<DSpaceRunnable, ScriptRe
ParameterRest parameterRest = new ParameterRest(); ParameterRest parameterRest = new ParameterRest();
parameterRest.setDescription(option.getDescription()); parameterRest.setDescription(option.getDescription());
parameterRest.setName((option.getOpt() != null ? "-" + option.getOpt() : "--" + option.getLongOpt())); parameterRest.setName((option.getOpt() != null ? "-" + option.getOpt() : "--" + option.getLongOpt()));
parameterRest.setNameLong(option.getLongOpt() != null ? "--" + option.getLongOpt() : null);
parameterRest.setType(((Class) option.getType()).getSimpleName()); parameterRest.setType(((Class) option.getType()).getSimpleName());
parameterRest.setMandatory(option.isRequired());
parameterRestList.add(parameterRest); parameterRestList.add(parameterRest);
} }
scriptRest.setParameterRestList(parameterRestList); scriptRest.setParameterRestList(parameterRestList);

View File

@@ -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.converter;
import java.sql.SQLException;
import org.apache.log4j.Logger;
import org.dspace.app.rest.model.VersionRest;
import org.dspace.app.rest.projection.Projection;
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.ConfigurationService;
import org.dspace.services.RequestService;
import org.dspace.services.model.Request;
import org.dspace.versioning.Version;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* This is the converter that takes care of the conversion between {@link Version} and {@link VersionRest}
*/
@Component
public class VersionConverter implements DSpaceConverter<Version, VersionRest> {
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<Version> getModelClass() {
return Version.class;
}
}

View File

@@ -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<VersionHistory, VersionHistoryRest> {
@Override
public VersionHistoryRest convert(VersionHistory modelObject, Projection projection) {
VersionHistoryRest versionHistoryRest = new VersionHistoryRest();
versionHistoryRest.setId(modelObject.getID());
return versionHistoryRest;
}
@Override
public Class<VersionHistory> getModelClass() {
return VersionHistory.class;
}
}

View File

@@ -35,7 +35,7 @@ public class AuthnHalLinkFactory extends HalLinkFactory<AuthnResource, Authentic
.logout())); .logout()));
list.add(buildLink("status", methodOn list.add(buildLink("status", methodOn
.status(null))); .status(null, null)));
} }
@Override @Override

View File

@@ -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.model;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import org.dspace.app.rest.RestResourceController;
/**
* The Authorization Feature REST Resource, shortly feature. 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 class AuthorizationFeatureRest extends BaseObjectRest<String> {
public static final String NAME = "feature";
public static final String CATEGORY = RestAddressableModel.AUTHORIZATION;
private String description;
@JsonProperty(value = "resourcetypes")
private List<String> 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<String> getResourceTypes() {
return resourceTypes;
}
public void setResourceTypes(List<String> resourceTypes) {
this.resourceTypes = resourceTypes;
}
}

View File

@@ -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<String> {
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;
}
}

View File

@@ -33,6 +33,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
name = ItemRest.RELATIONSHIPS, name = ItemRest.RELATIONSHIPS,
method = "getRelationships" method = "getRelationships"
), ),
@LinkRest(
name = ItemRest.VERSION,
method = "getItemVersion"
),
@LinkRest( @LinkRest(
name = ItemRest.TEMPLATE_ITEM_OF, name = ItemRest.TEMPLATE_ITEM_OF,
method = "getTemplateItemOf" method = "getTemplateItemOf"
@@ -47,6 +51,7 @@ public class ItemRest extends DSpaceObjectRest {
public static final String MAPPED_COLLECTIONS = "mappedCollections"; public static final String MAPPED_COLLECTIONS = "mappedCollections";
public static final String OWNING_COLLECTION = "owningCollection"; public static final String OWNING_COLLECTION = "owningCollection";
public static final String RELATIONSHIPS = "relationships"; public static final String RELATIONSHIPS = "relationships";
public static final String VERSION = "version";
public static final String TEMPLATE_ITEM_OF = "templateItemOf"; public static final String TEMPLATE_ITEM_OF = "templateItemOf";
private boolean inArchive = false; private boolean inArchive = false;

View File

@@ -25,6 +25,16 @@ public class ParameterRest {
*/ */
private String type; 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() { public String getName() {
return name; return name;
} }
@@ -48,4 +58,36 @@ public class ParameterRest {
public void setType(String type) { public void setType(String type) {
this.type = 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;
}
} }

View File

@@ -44,4 +44,16 @@ public abstract class RestAddressableModel implements RestModel {
public void setProjection(Projection projection) { public void setProjection(Projection projection) {
this.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();
}
} }

View File

@@ -31,6 +31,7 @@ public interface RestModel extends Serializable {
public static final String SYSTEM = "system"; public static final String SYSTEM = "system";
public static final String WORKFLOW = "workflow"; public static final String WORKFLOW = "workflow";
public static final String AUTHORIZATION = "authz"; public static final String AUTHORIZATION = "authz";
public static final String VERSIONING = "versioning";
public String getType(); public String getType();

View File

@@ -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<Integer> {
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;
}
}

View File

@@ -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<Integer> {
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;
}
}

View File

@@ -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<AuthorizationFeatureRest> {
public AuthorizationFeatureResource(AuthorizationFeatureRest authzFeature, Utils utils) {
super(authzFeature, utils);
}
}

View File

@@ -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<AuthorizationRest> {
public AuthorizationResource(AuthorizationRest authz, Utils utils) {
super(authz, utils);
}
}

View File

@@ -12,7 +12,7 @@ import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource;
import org.dspace.app.rest.utils.Utils; 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 * adding support for the links and embedded resources
* *
* @author Andrea Bollini (andrea.bollini at 4science.it) * @author Andrea Bollini (andrea.bollini at 4science.it)

View File

@@ -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<VersionHistoryRest> {
public VersionHistoryResource(VersionHistoryRest data, Utils utils) {
super(data, utils);
}
}

View File

@@ -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<VersionRest> {
public VersionResource(VersionRest data, Utils utils) {
super(data, utils);
}
}

View File

@@ -8,7 +8,6 @@
package org.dspace.app.rest.model.step; package org.dspace.app.rest.model.step;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -93,4 +92,4 @@ public class UploadBitstreamRest extends UploadStatusResponse {
public void setFormat(BitstreamFormatRest format) { public void setFormat(BitstreamFormatRest format) {
this.format = format; this.format = format;
} }
} }

View File

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

View File

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

View File

@@ -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<AuthorizationFeatureRest, String> {
@Autowired
private AuthorizationFeatureService authorizationFeatureService;
@Autowired
protected ConverterService converter;
@Override
public Class<AuthorizationFeatureRest> getDomainClass() {
return AuthorizationFeatureRest.class;
}
@PreAuthorize("hasAuthority('ADMIN')")
@Override
public Page<AuthorizationFeatureRest> 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<AuthorizationFeatureRest> findByResourceType(@Parameter(value = "type", required = true) String type,
Pageable pageable) {
List<AuthorizationFeature> foundFeatures = authorizationFeatureService.findByResourceType(type);
return converter.toRestPage(utils.getPage(foundFeatures, pageable), utils.obtainProjection());
}
}

View File

@@ -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;
}
}

View File

@@ -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<AuthorizationRest, String> {
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<AuthorizationRest> 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<AuthorizationFeature> features = authorizationFeatureService.findByResourceType(obj.getUniqueType());
List<Authorization> authorizations = new ArrayList<Authorization>();
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<AuthorizationRest> getDomainClass() {
return AuthorizationRest.class;
}
@Override
public Page<AuthorizationRest> findAll(Context context, Pageable pageable) {
throw new RepositoryMethodNotImplementedException(AuthorizationRest.NAME, "findAll");
}
}

View File

@@ -9,6 +9,7 @@ package org.dspace.app.rest.repository;
import java.io.IOException; import java.io.IOException;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.servlet.ServletInputStream; 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.model.wrapper.TemplateItem;
import org.dspace.app.rest.utils.CollectionRestEqualityUtils; import org.dspace.app.rest.utils.CollectionRestEqualityUtils;
import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Bitstream; import org.dspace.content.Bitstream;
import org.dspace.content.Collection; import org.dspace.content.Collection;
import org.dspace.content.Community; import org.dspace.content.Community;
@@ -39,6 +41,12 @@ import org.dspace.content.service.CommunityService;
import org.dspace.content.service.ItemService; import org.dspace.content.service.ItemService;
import org.dspace.core.Constants; import org.dspace.core.Constants;
import org.dspace.core.Context; 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.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@@ -71,6 +79,12 @@ public class CollectionRestRepository extends DSpaceObjectRestRepository<Collect
@Autowired @Autowired
private ItemService itemService; private ItemService itemService;
@Autowired
SearchService searchService;
@Autowired
AuthorizeService authorizeService;
public CollectionRestRepository(CollectionService dsoService) { public CollectionRestRepository(CollectionService dsoService) {
super(dsoService); super(dsoService);
} }
@@ -93,11 +107,28 @@ public class CollectionRestRepository extends DSpaceObjectRestRepository<Collect
@Override @Override
public Page<CollectionRest> findAll(Context context, Pageable pageable) { public Page<CollectionRest> findAll(Context context, Pageable pageable) {
try { try {
long total = cs.countTotal(context); if (authorizeService.isAdmin(context)) {
List<Collection> collections = cs.findAll(context, pageable.getPageSize(), long total = cs.countTotal(context);
Math.toIntExact(pageable.getOffset())); List<Collection> collections = cs.findAll(context, pageable.getPageSize(),
return converter.toRestPage(collections, pageable, total, utils.obtainProjection()); Math.toIntExact(pageable.getOffset()));
} catch (SQLException e) { return converter.toRestPage(collections, pageable, total, utils.obtainProjection());
} else {
List<Collection> collections = new LinkedList<Collection>();
// 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); throw new RuntimeException(e.getMessage(), e);
} }
} }

View File

@@ -8,6 +8,7 @@
package org.dspace.app.rest.repository; package org.dspace.app.rest.repository;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -20,6 +21,13 @@ import org.dspace.content.Collection;
import org.dspace.content.Community; import org.dspace.content.Community;
import org.dspace.content.service.CommunityService; import org.dspace.content.service.CommunityService;
import org.dspace.core.Context; 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.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@@ -37,6 +45,9 @@ public class CommunityCollectionLinkRepository extends AbstractDSpaceRestReposit
@Autowired @Autowired
CommunityService communityService; CommunityService communityService;
@Autowired
SearchService searchService;
@PreAuthorize("hasPermission(#communityId, 'COMMUNITY', 'READ')") @PreAuthorize("hasPermission(#communityId, 'COMMUNITY', 'READ')")
public Page<CollectionRest> getCollections(@Nullable HttpServletRequest request, public Page<CollectionRest> getCollections(@Nullable HttpServletRequest request,
UUID communityId, UUID communityId,
@@ -48,10 +59,27 @@ public class CommunityCollectionLinkRepository extends AbstractDSpaceRestReposit
if (community == null) { if (community == null) {
throw new ResourceNotFoundException("No such community: " + communityId); throw new ResourceNotFoundException("No such community: " + communityId);
} }
List<Collection> collections = community.getCollections(); Pageable pageable = utils.getPageable(optionalPageable);
return converter.toRestPage(utils.getPage(collections, optionalPageable), projection); List<Collection> collections = new LinkedList<Collection>();
} catch (SQLException e) { IndexObjectFactoryFactory indexObjectFactory = IndexObjectFactoryFactory.getInstance();
throw new RuntimeException(e); 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);
} }
} }
} }

View File

@@ -9,6 +9,7 @@ package org.dspace.app.rest.repository;
import java.io.IOException; import java.io.IOException;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.servlet.ServletInputStream; import javax.servlet.ServletInputStream;
@@ -17,7 +18,6 @@ import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.dspace.app.rest.Parameter;
import org.dspace.app.rest.SearchRestMethod; import org.dspace.app.rest.SearchRestMethod;
import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException; 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.model.patch.Patch;
import org.dspace.app.rest.utils.CommunityRestEqualityUtils; import org.dspace.app.rest.utils.CommunityRestEqualityUtils;
import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Bitstream; import org.dspace.content.Bitstream;
import org.dspace.content.Community; import org.dspace.content.Community;
import org.dspace.content.service.BitstreamService; import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.CommunityService; import org.dspace.content.service.CommunityService;
import org.dspace.core.Context; 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.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@@ -59,6 +66,11 @@ public class CommunityRestRepository extends DSpaceObjectRestRepository<Communit
CommunityRestEqualityUtils communityRestEqualityUtils; CommunityRestEqualityUtils communityRestEqualityUtils;
@Autowired @Autowired
SearchService searchService;
@Autowired
AuthorizeService authorizeService;
private CommunityService cs; private CommunityService cs;
public CommunityRestRepository(CommunityService dsoService) { public CommunityRestRepository(CommunityService dsoService) {
@@ -147,11 +159,28 @@ public class CommunityRestRepository extends DSpaceObjectRestRepository<Communit
@Override @Override
public Page<CommunityRest> findAll(Context context, Pageable pageable) { public Page<CommunityRest> findAll(Context context, Pageable pageable) {
try { try {
long total = cs.countTotal(context); if (authorizeService.isAdmin(context)) {
List<Community> communities = cs.findAll(context, pageable.getPageSize(), long total = cs.countTotal(context);
List<Community> communities = cs.findAll(context, pageable.getPageSize(),
Math.toIntExact(pageable.getOffset())); Math.toIntExact(pageable.getOffset()));
return converter.toRestPage(communities, pageable, total, utils.obtainProjection()); return converter.toRestPage(communities, pageable, total, utils.obtainProjection());
} catch (SQLException e) { } else {
List<Community> communities = new LinkedList<Community>();
// 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); throw new RuntimeException(e.getMessage(), e);
} }
} }
@@ -168,25 +197,6 @@ public class CommunityRestRepository extends DSpaceObjectRestRepository<Communit
} }
} }
// TODO: add method in dspace api to support direct query for subcommunities
// with pagination and authorization check
@SearchRestMethod(name = "subCommunities")
public Page<CommunityRest> 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<Community> subCommunities = community.getSubcommunities();
return converter.toRestPage(utils.getPage(subCommunities, pageable), utils.obtainProjection());
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
@Override @Override
@PreAuthorize("hasPermission(#id, 'COMMUNITY', 'WRITE')") @PreAuthorize("hasPermission(#id, 'COMMUNITY', 'WRITE')")
protected void patch(Context context, HttpServletRequest request, String apiCategory, String model, UUID id, protected void patch(Context context, HttpServletRequest request, String apiCategory, String model, UUID id,

View File

@@ -8,6 +8,7 @@
package org.dspace.app.rest.repository; package org.dspace.app.rest.repository;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -18,6 +19,13 @@ import org.dspace.app.rest.projection.Projection;
import org.dspace.content.Community; import org.dspace.content.Community;
import org.dspace.content.service.CommunityService; import org.dspace.content.service.CommunityService;
import org.dspace.core.Context; 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.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@@ -35,6 +43,9 @@ public class CommunitySubcommunityLinkRepository extends AbstractDSpaceRestRepos
@Autowired @Autowired
CommunityService communityService; CommunityService communityService;
@Autowired
SearchService searchService;
@PreAuthorize("hasPermission(#communityId, 'COMMUNITY', 'READ')") @PreAuthorize("hasPermission(#communityId, 'COMMUNITY', 'READ')")
public Page<CommunityRest> getSubcommunities(@Nullable HttpServletRequest request, public Page<CommunityRest> getSubcommunities(@Nullable HttpServletRequest request,
UUID communityId, UUID communityId,
@@ -46,10 +57,25 @@ public class CommunitySubcommunityLinkRepository extends AbstractDSpaceRestRepos
if (community == null) { if (community == null) {
throw new ResourceNotFoundException("No such community: " + communityId); throw new ResourceNotFoundException("No such community: " + communityId);
} }
List<Community> subcommunities = community.getSubcommunities(); Pageable pageable = utils.getPageable(optionalPageable);
return converter.toRestPage(utils.getPage(subcommunities, optionalPageable), projection); List<Community> publicSubcommunities = new LinkedList<Community>();
} catch (SQLException e) { IndexObjectFactoryFactory indexObjectFactory = IndexObjectFactoryFactory.getInstance();
throw new RuntimeException(e); 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);
} }
} }
} }

View File

@@ -29,7 +29,7 @@ import org.springframework.data.rest.webmvc.ResourceNotFoundException;
* @param <R> the corresponding DSpaceObjectRest. * @param <R> the corresponding DSpaceObjectRest.
*/ */
public abstract class DSpaceObjectRestRepository<M extends DSpaceObject, R extends DSpaceObjectRest> public abstract class DSpaceObjectRestRepository<M extends DSpaceObject, R extends DSpaceObjectRest>
extends DSpaceRestRepository<R, UUID> { extends DSpaceRestRepository<R, UUID> implements ReloadableEntityObjectRepository<M, UUID> {
final DSpaceObjectService<M> dsoService; final DSpaceObjectService<M> dsoService;
@@ -64,4 +64,14 @@ public abstract class DSpaceObjectRestRepository<M extends DSpaceObject, R exten
resourcePatch.patch(obtainContext(), dso, patch.getOperations()); resourcePatch.patch(obtainContext(), dso, patch.getOperations());
dsoService.update(obtainContext(), dso); dsoService.update(obtainContext(), dso);
} }
@Override
public M findDomainObjectByPk(Context context, UUID uuid) throws SQLException {
return dsoService.find(context, uuid);
}
@Override
public Class<UUID> getPKClass() {
return UUID.class;
}
} }

View File

@@ -14,7 +14,6 @@ import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
@@ -382,7 +381,8 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
* @return the new state of the REST object * @return the new state of the REST object
*/ */
public T upload(HttpServletRequest request, String apiCategory, String model, public T upload(HttpServletRequest request, String apiCategory, String model,
ID id, MultipartFile file) throws SQLException { ID id, MultipartFile file)
throws SQLException, FileNotFoundException, IOException, AuthorizeException {
throw new RuntimeException("No implementation found; Method not allowed!"); throw new RuntimeException("No implementation found; Method not allowed!");
} }

View File

@@ -0,0 +1,68 @@
/**
* 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.UUID;
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.content.service.ItemService;
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 is the Repository that will take care of fetching the Version for a given Item
*/
@Component(ItemRest.CATEGORY + "." + ItemRest.NAME + "." + ItemRest.VERSION)
public class ItemVersionLinkRepository extends AbstractDSpaceRestRepository
implements LinkRestRepository {
@Autowired
private VersioningService versioningService;
@Autowired
private ItemService itemService;
/**
* This method will return the VersionRest object from the Item that is associated with the given itemUuid
* @param request The current request
* @param itemUuid The itemUuid used to find the Item for which we'll return the VersionRest object
* @param optionalPageable Pageable if present
* @param projection Current Projection
* @return The VersionRest object constructed from the Version object for the Item that has the
* itemUuid param as UUID
* @throws SQLException If something goes wrong
*/
public VersionRest getItemVersion(@Nullable HttpServletRequest request,
UUID itemUuid,
@Nullable Pageable optionalPageable,
Projection projection) throws SQLException {
Context context = obtainContext();
Item item = itemService.find(context, itemUuid);
if (item == null) {
throw new ResourceNotFoundException("The Item for uuid: " + itemUuid + " couldn't be found");
}
Version version = versioningService.getVersion(context, item);
if (version == null) {
return null;
}
return converter.toRest(version, projection);
}
}

View File

@@ -0,0 +1,43 @@
/**
* 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.io.Serializable;
import java.sql.SQLException;
import org.dspace.core.Context;
import org.dspace.core.ReloadableEntity;
/**
* This interface must be implemented by all the rest repository that need to
* provide access to the DSpace API model objects corresponding to the REST
* resources that it manages
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
* @param <T> the ReloadableEntity type
* @param <PK> the primary key type
*/
public interface ReloadableEntityObjectRepository<T extends ReloadableEntity<PK>,
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<PK> getPKClass();
}

View File

@@ -11,11 +11,9 @@ import java.io.IOException;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.dspace.app.rest.Parameter; import org.dspace.app.rest.Parameter;
import org.dspace.app.rest.SearchRestMethod; import org.dspace.app.rest.SearchRestMethod;
import org.dspace.app.rest.exception.MissingParameterException; import org.dspace.app.rest.exception.MissingParameterException;

View File

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

View File

@@ -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<VersionHistoryRest, Integer> {
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<VersionHistoryRest> findAll(Context context, Pageable pageable) {
throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", "");
}
@Override
public Class<VersionHistoryRest> getDomainClass() {
return VersionHistoryRest.class;
}
}

View File

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

View File

@@ -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<VersionRest, Integer> {
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<VersionRest> findAll(Context context, Pageable pageable) {
throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", "");
}
@Override
public Class<VersionRest> getDomainClass() {
return VersionRest.class;
}
}

View File

@@ -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<VersionRest> 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<Version> versions = versioningService.getVersionsByHistory(context, versionHistory);
Pageable pageable = optionalPageable != null ? optionalPageable : new PageRequest(0, 20);
return converter.toRestPage(utils.getPage(versions, pageable), projection);
}
}

View File

@@ -34,9 +34,12 @@ import org.dspace.app.util.SubmissionConfigReader;
import org.dspace.app.util.SubmissionConfigReaderException; import org.dspace.app.util.SubmissionConfigReaderException;
import org.dspace.app.util.SubmissionStepConfig; import org.dspace.app.util.SubmissionStepConfig;
import org.dspace.authorize.AuthorizeException; 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.BitstreamFormatService;
import org.dspace.content.service.BitstreamService; import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.ItemService; import org.dspace.content.service.ItemService;
import org.dspace.core.Constants;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.EPersonServiceImpl; 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.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.rest.webmvc.ResourceNotFoundException; import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@@ -97,6 +101,9 @@ public class WorkflowItemRestRepository extends DSpaceRestRepository<WorkflowIte
@Autowired @Autowired
WorkflowService<XmlWorkflowItem> wfs; WorkflowService<XmlWorkflowItem> wfs;
@Autowired
AuthorizeService authorizeService;
@Autowired @Autowired
ClaimedTaskService claimedTaskService; ClaimedTaskService claimedTaskService;
@@ -188,6 +195,7 @@ public class WorkflowItemRestRepository extends DSpaceRestRepository<WorkflowIte
@Override @Override
public WorkflowItemRest upload(HttpServletRequest request, String apiCategory, String model, Integer id, public WorkflowItemRest upload(HttpServletRequest request, String apiCategory, String model, Integer id,
MultipartFile file) throws SQLException { MultipartFile file) throws SQLException {
Context context = obtainContext(); Context context = obtainContext();
WorkflowItemRest wsi = findOne(context, id); WorkflowItemRest wsi = findOne(context, id);
XmlWorkflowItem source = wis.find(context, id); XmlWorkflowItem source = wis.find(context, id);
@@ -353,4 +361,33 @@ public class WorkflowItemRestRepository extends DSpaceRestRepository<WorkflowIte
+ "#checkIfEditMetadataAllowedInCurrentStep trying to retrieve workflow configuration from config", e); + "#checkIfEditMetadataAllowedInCurrentStep trying to retrieve workflow configuration from config", e);
} }
} }
/**
* This is a search method that will return the WorkflowItemRest object found through the UUID of an item. It'll
* find the Item through the given UUID and try to resolve the WorkflowItem relevant for that item and return it.
* It'll return a 401/403 if the current user isn't allowed to view the WorkflowItem.
* It'll return a 204 if nothing was found
* @param itemUuid The UUID for the Item to be used
* @param pageable The pageable if present
* @return The resulting WorkflowItemRest object
*/
@SearchRestMethod(name = "item")
public WorkflowItemRest findByItemUuid(@Parameter(value = "uuid", required = true) UUID itemUuid,
Pageable pageable) {
try {
Context context = obtainContext();
Item item = itemService.find(context, itemUuid);
XmlWorkflowItem xmlWorkflowItem = wis.findByItem(context, item);
if (xmlWorkflowItem == null) {
return null;
}
if (!authorizeService.authorizeActionBoolean(context, xmlWorkflowItem.getItem(), Constants.READ)) {
throw new AccessDeniedException("The current user does not have rights to view the WorkflowItem");
}
return converter.toRest(xmlWorkflowItem, utils.obtainProjection());
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
} }

View File

@@ -41,7 +41,9 @@ import org.dspace.app.util.SubmissionConfigReader;
import org.dspace.app.util.SubmissionConfigReaderException; import org.dspace.app.util.SubmissionConfigReaderException;
import org.dspace.app.util.SubmissionStepConfig; import org.dspace.app.util.SubmissionStepConfig;
import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Collection; import org.dspace.content.Collection;
import org.dspace.content.Item;
import org.dspace.content.WorkspaceItem; import org.dspace.content.WorkspaceItem;
import org.dspace.content.service.BitstreamFormatService; import org.dspace.content.service.BitstreamFormatService;
import org.dspace.content.service.BitstreamService; import org.dspace.content.service.BitstreamService;
@@ -65,6 +67,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.rest.webmvc.json.patch.PatchException; import org.springframework.data.rest.webmvc.json.patch.PatchException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@@ -74,7 +78,8 @@ import org.springframework.web.multipart.MultipartFile;
* @author Andrea Bollini (andrea.bollini at 4science.it) * @author Andrea Bollini (andrea.bollini at 4science.it)
*/ */
@Component(WorkspaceItemRest.CATEGORY + "." + WorkspaceItemRest.NAME) @Component(WorkspaceItemRest.CATEGORY + "." + WorkspaceItemRest.NAME)
public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceItemRest, Integer> { public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceItemRest, Integer>
implements ReloadableEntityObjectRepository<WorkspaceItem, Integer> {
public static final String OPERATION_PATH_SECTIONS = "sections"; public static final String OPERATION_PATH_SECTIONS = "sections";
@@ -110,6 +115,9 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
@Autowired @Autowired
CollectionService collectionService; CollectionService collectionService;
@Autowired
AuthorizeService authorizeService;
@Autowired @Autowired
private UriListHandlerService uriListHandlerService; private UriListHandlerService uriListHandlerService;
@@ -119,7 +127,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
submissionConfigReader = new SubmissionConfigReader(); submissionConfigReader = new SubmissionConfigReader();
} }
//TODO @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'READ')") @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'READ')")
@Override @Override
public WorkspaceItemRest findOne(Context context, Integer id) { public WorkspaceItemRest findOne(Context context, Integer id) {
WorkspaceItem witem = null; WorkspaceItem witem = null;
@@ -134,7 +142,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
return converter.toRest(witem, utils.obtainProjection()); return converter.toRest(witem, utils.obtainProjection());
} }
//TODO @PreAuthorize("hasAuthority('ADMIN')") @PreAuthorize("hasAuthority('ADMIN')")
@Override @Override
public Page<WorkspaceItemRest> findAll(Context context, Pageable pageable) { public Page<WorkspaceItemRest> findAll(Context context, Pageable pageable) {
try { try {
@@ -147,7 +155,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
} }
} }
//TODO @PreAuthorize("hasPermission(#submitterID, 'EPERSON', 'READ')") @PreAuthorize("hasPermission(#submitterID, 'EPERSON', 'READ')")
@SearchRestMethod(name = "findBySubmitter") @SearchRestMethod(name = "findBySubmitter")
public Page<WorkspaceItemRest> findBySubmitter(@Parameter(value = "uuid", required = true) UUID submitterID, public Page<WorkspaceItemRest> findBySubmitter(@Parameter(value = "uuid", required = true) UUID submitterID,
Pageable pageable) { Pageable pageable) {
@@ -213,7 +221,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
return WorkspaceItemRest.class; return WorkspaceItemRest.class;
} }
//TODO @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'WRITE')") @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'WRITE')")
@Override @Override
public WorkspaceItemRest upload(HttpServletRequest request, String apiCategory, String model, Integer id, public WorkspaceItemRest upload(HttpServletRequest request, String apiCategory, String model, Integer id,
MultipartFile file) throws SQLException { MultipartFile file) throws SQLException {
@@ -263,7 +271,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
return wsi; return wsi;
} }
//TODO @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'WRITE')") @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'WRITE')")
@Override @Override
public void patch(Context context, HttpServletRequest request, String apiCategory, String model, Integer id, public void patch(Context context, HttpServletRequest request, String apiCategory, String model, Integer id,
Patch patch) throws SQLException, AuthorizeException { Patch patch) throws SQLException, AuthorizeException {
@@ -326,7 +334,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
} }
} }
//TODO @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'DELETE')") @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'DELETE')")
@Override @Override
protected void delete(Context context, Integer id) throws AuthorizeException { protected void delete(Context context, Integer id) throws AuthorizeException {
WorkspaceItem witem = null; WorkspaceItem witem = null;
@@ -496,5 +504,41 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
return converter.toRest(workspaceItem, utils.obtainProjection()); return converter.toRest(workspaceItem, utils.obtainProjection());
} }
/**
* This is a search method that will return the WorkspaceItemRest object found through the UUID of an item. It'll
* find the Item through the given UUID and try to resolve the WorkspaceItem relevant for that item and return it.
* It'll return a 401/403 if the current user isn't allowed to view the WorkspaceItem.
* It'll return a 204 if nothing was found
* @param itemUuid The UUID for the Item to be used
* @param pageable The pageable if present
* @return The resulting WorkspaceItem object
*/
@SearchRestMethod(name = "item")
public WorkspaceItemRest findByItemUuid(@Parameter(value = "uuid", required = true) UUID itemUuid,
Pageable pageable) {
try {
Context context = obtainContext();
Item item = itemService.find(context, itemUuid);
WorkspaceItem workspaceItem = wis.findByItem(context, item);
if (workspaceItem == null) {
return null;
}
if (!authorizeService.authorizeActionBoolean(context, workspaceItem.getItem(), Constants.READ)) {
throw new AccessDeniedException("The current user does not have rights to view the WorkflowItem");
}
return converter.toRest(workspaceItem, utils.obtainProjection());
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
@Override
public WorkspaceItem findDomainObjectByPk(Context context, Integer id) throws SQLException {
return wis.find(context, id);
}
@Override
public Class<Integer> getPKClass() {
return Integer.class;
}
} }

View File

@@ -15,7 +15,6 @@ import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.repository.patch.operation.PatchOperation; import org.dspace.app.rest.repository.patch.operation.PatchOperation;
import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.ResourcePolicy;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;

View File

@@ -45,7 +45,7 @@ public class CustomLogoutHandler implements LogoutHandler {
Authentication authentication) { Authentication authentication) {
try { try {
Context context = ContextUtil.obtainContext(httpServletRequest); Context context = ContextUtil.obtainContext(httpServletRequest);
restAuthenticationService.invalidateAuthenticationData(httpServletRequest, context); restAuthenticationService.invalidateAuthenticationData(httpServletRequest, httpServletResponse, context);
context.commit(); context.commit();
} catch (Exception e) { } catch (Exception e) {

View File

@@ -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;
}
}

View File

@@ -8,7 +8,6 @@
package org.dspace.app.rest.security; package org.dspace.app.rest.security;
import java.io.Serializable; import java.io.Serializable;
import java.sql.SQLException; import java.sql.SQLException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -29,9 +28,9 @@ import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* *
* {@link RestPermissionEvaluatorPlugin} class that evaluate ADMIN permissions over a Resource Policy * {@link RestPermissionEvaluatorPlugin} class that evaluate ADMIN permissions over a Resource Policy
* *
* @author Mykhaylo Boychuk - (4Science.it) * @author Mykhaylo Boychuk - (4Science.it)
*/ */
@Component @Component

View File

@@ -26,13 +26,14 @@ import org.springframework.stereotype.Service;
public interface RestAuthenticationService { public interface RestAuthenticationService {
void addAuthenticationDataForUser(HttpServletRequest request, HttpServletResponse response, void addAuthenticationDataForUser(HttpServletRequest request, HttpServletResponse response,
DSpaceAuthentication authentication) throws IOException; DSpaceAuthentication authentication, boolean addCookie) throws IOException;
EPerson getAuthenticatedEPerson(HttpServletRequest request, Context context); EPerson getAuthenticatedEPerson(HttpServletRequest request, Context context);
boolean hasAuthenticationData(HttpServletRequest request); boolean hasAuthenticationData(HttpServletRequest request);
void invalidateAuthenticationData(HttpServletRequest request, Context context) throws Exception; void invalidateAuthenticationData(HttpServletRequest request, HttpServletResponse response, Context context)
throws Exception;
AuthenticationService getAuthenticationService(); AuthenticationService getAuthenticationService();
@@ -44,4 +45,6 @@ public interface RestAuthenticationService {
*/ */
String getWwwAuthenticateHeaderValue(HttpServletRequest request, HttpServletResponse response); String getWwwAuthenticateHeaderValue(HttpServletRequest request, HttpServletResponse response);
void invalidateAuthenticationCookie(HttpServletResponse res);
} }

View File

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

View File

@@ -61,6 +61,7 @@ public class StatelessAuthenticationFilter extends BasicAuthenticationFilter {
Authentication authentication = getAuthentication(req); Authentication authentication = getAuthentication(req);
if (authentication != null) { if (authentication != null) {
SecurityContextHolder.getContext().setAuthentication(authentication); SecurityContextHolder.getContext().setAuthentication(authentication);
restAuthenticationService.invalidateAuthenticationCookie(res);
} }
chain.doFilter(req, res); chain.doFilter(req, res);

View File

@@ -28,9 +28,9 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
*/ */
public class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter { public class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter {
private AuthenticationManager authenticationManager; protected AuthenticationManager authenticationManager;
private RestAuthenticationService restAuthenticationService; protected RestAuthenticationService restAuthenticationService;
@Override @Override
public void afterPropertiesSet() { public void afterPropertiesSet() {
@@ -63,7 +63,7 @@ public class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter
Authentication auth) throws IOException, ServletException { Authentication auth) throws IOException, ServletException {
DSpaceAuthentication dSpaceAuthentication = (DSpaceAuthentication) auth; DSpaceAuthentication dSpaceAuthentication = (DSpaceAuthentication) auth;
restAuthenticationService.addAuthenticationDataForUser(req, res, dSpaceAuthentication); restAuthenticationService.addAuthenticationDataForUser(req, res, dSpaceAuthentication, false);
} }
@Override @Override

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -109,6 +109,12 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
restAuthenticationService), restAuthenticationService),
LogoutFilter.class) 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 // Add a custom Token based authentication filter based on the token previously given to the client
// before each URL // before each URL
.addFilterBefore(new StatelessAuthenticationFilter(authenticationManager(), restAuthenticationService, .addFilterBefore(new StatelessAuthenticationFilter(authenticationManager(), restAuthenticationService,

Some files were not shown because too many files have changed in this diff Show More