Merge remote-tracking branch 'upstream/master' into Manage-groups-2

This commit is contained in:
Antoine Snyers
2020-04-01 12:57:58 +02:00
160 changed files with 9748 additions and 576 deletions

View File

@@ -10,6 +10,7 @@ package org.dspace.app.rest;
import java.sql.SQLException;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.converter.EPersonConverter;
@@ -20,6 +21,7 @@ import org.dspace.app.rest.model.EPersonRest;
import org.dspace.app.rest.model.hateoas.AuthenticationStatusResource;
import org.dspace.app.rest.model.hateoas.AuthnResource;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.security.RestAuthenticationService;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.Utils;
import org.dspace.core.Context;
@@ -60,6 +62,9 @@ public class AuthenticationRestController implements InitializingBean {
@Autowired
private HalLinkService halLinkService;
@Autowired
private RestAuthenticationService restAuthenticationService;
@Autowired
private Utils utils;
@@ -77,7 +82,8 @@ public class AuthenticationRestController implements InitializingBean {
}
@RequestMapping(value = "/status", method = RequestMethod.GET)
public AuthenticationStatusResource status(HttpServletRequest request) throws SQLException {
public AuthenticationStatusResource status(HttpServletRequest request, HttpServletResponse response)
throws SQLException {
Context context = ContextUtil.obtainContext(request);
EPersonRest ePersonRest = null;
Projection projection = utils.obtainProjection();
@@ -86,6 +92,14 @@ public class AuthenticationRestController implements InitializingBean {
}
AuthenticationStatusRest authenticationStatusRest = new AuthenticationStatusRest(ePersonRest);
// Whether authentication status is false add WWW-Authenticate so client can retrieve the available
// authentication methods
if (!authenticationStatusRest.isAuthenticated()) {
String authenticateHeaderValue = restAuthenticationService
.getWwwAuthenticateHeaderValue(request, response);
response.setHeader("WWW-Authenticate", authenticateHeaderValue);
}
authenticationStatusRest.setProjection(projection);
AuthenticationStatusResource authenticationStatusResource = converter.toResource(authenticationStatusRest);

View File

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

View File

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

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

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.setDescription(option.getDescription());
parameterRest.setName((option.getOpt() != null ? "-" + option.getOpt() : "--" + option.getLongOpt()));
parameterRest.setNameLong(option.getLongOpt() != null ? "--" + option.getLongOpt() : null);
parameterRest.setType(((Class) option.getType()).getSimpleName());
parameterRest.setMandatory(option.isRequired());
parameterRestList.add(parameterRest);
}
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()));
list.add(buildLink("status", methodOn
.status(null)));
.status(null, null)));
}
@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,
method = "getRelationships"
),
@LinkRest(
name = ItemRest.VERSION,
method = "getItemVersion"
),
@LinkRest(
name = ItemRest.TEMPLATE_ITEM_OF,
method = "getTemplateItemOf"
@@ -47,6 +51,7 @@ public class ItemRest extends DSpaceObjectRest {
public static final String MAPPED_COLLECTIONS = "mappedCollections";
public static final String OWNING_COLLECTION = "owningCollection";
public static final String RELATIONSHIPS = "relationships";
public static final String VERSION = "version";
public static final String TEMPLATE_ITEM_OF = "templateItemOf";
private boolean inArchive = false;

View File

@@ -25,6 +25,16 @@ public class ParameterRest {
*/
private String type;
/**
* The long name of the parameter
*/
private String nameLong;
/**
* Boolean indicating whether the parameter is mandatory or not
*/
private boolean mandatory;
public String getName() {
return name;
}
@@ -48,4 +58,36 @@ public class ParameterRest {
public void setType(String type) {
this.type = type;
}
/**
* Generic getter for the nameLong
* @return the nameLong value of this ParameterRest
*/
public String getNameLong() {
return nameLong;
}
/**
* Generic setter for the nameLong
* @param nameLong The nameLong to be set on this ParameterRest
*/
public void setNameLong(String nameLong) {
this.nameLong = nameLong;
}
/**
* Generic getter for the mandatory
* @return the mandatory value of this ParameterRest
*/
public boolean isMandatory() {
return mandatory;
}
/**
* Generic setter for the mandatory
* @param mandatory The mandatory to be set on this ParameterRest
*/
public void setMandatory(boolean mandatory) {
this.mandatory = mandatory;
}
}

View File

@@ -44,4 +44,16 @@ public abstract class RestAddressableModel implements RestModel {
public void setProjection(Projection projection) {
this.projection = projection;
}
@JsonIgnore
/**
*
* @return an unique string useful to unambiguously identify the type of rest
* object. It is in the form of category.model where model is in its
* singular form. Examples include core.item, core.community,
* submission.workspaceitem, etc.
*/
public String getUniqueType() {
return getCategory() + "." + getType();
}
}

View File

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

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;
/**
* Item Rest HAL Resource. The HAL Resource wraps the REST Resource
* Collection Rest HAL Resource. The HAL Resource wraps the REST Resource
* adding support for the links and embedded resources
*
* @author Andrea Bollini (andrea.bollini at 4science.it)

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;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -93,4 +92,4 @@ public class UploadBitstreamRest extends UploadStatusResponse {
public void setFormat(BitstreamFormatRest format) {
this.format = format;
}
}
}

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

View File

@@ -8,6 +8,7 @@
package org.dspace.app.rest.repository;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
@@ -20,6 +21,13 @@ import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.service.CommunityService;
import org.dspace.core.Context;
import org.dspace.discovery.DiscoverQuery;
import org.dspace.discovery.DiscoverResult;
import org.dspace.discovery.IndexableObject;
import org.dspace.discovery.SearchService;
import org.dspace.discovery.SearchServiceException;
import org.dspace.discovery.indexobject.IndexableCollection;
import org.dspace.discovery.indexobject.factory.IndexObjectFactoryFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -37,6 +45,9 @@ public class CommunityCollectionLinkRepository extends AbstractDSpaceRestReposit
@Autowired
CommunityService communityService;
@Autowired
SearchService searchService;
@PreAuthorize("hasPermission(#communityId, 'COMMUNITY', 'READ')")
public Page<CollectionRest> getCollections(@Nullable HttpServletRequest request,
UUID communityId,
@@ -48,10 +59,27 @@ public class CommunityCollectionLinkRepository extends AbstractDSpaceRestReposit
if (community == null) {
throw new ResourceNotFoundException("No such community: " + communityId);
}
List<Collection> collections = community.getCollections();
return converter.toRestPage(utils.getPage(collections, optionalPageable), projection);
} catch (SQLException e) {
throw new RuntimeException(e);
Pageable pageable = utils.getPageable(optionalPageable);
List<Collection> collections = new LinkedList<Collection>();
IndexObjectFactoryFactory indexObjectFactory = IndexObjectFactoryFactory.getInstance();
IndexableObject scopeObject = indexObjectFactory.getIndexableObjects(context, community).get(0);
// search for all the collections direct children of our community
// and let the SOLR security plugins to limit what is returned to what the user can see
DiscoverQuery discoverQuery = new DiscoverQuery();
discoverQuery.setQuery("*:*");
discoverQuery.setDSpaceObjectFilter(IndexableCollection.TYPE);
discoverQuery.addFilterQueries("location.parent:" + communityId);
discoverQuery.setStart(Math.toIntExact(pageable.getOffset()));
discoverQuery.setMaxResults(pageable.getPageSize());
DiscoverResult resp = searchService.search(context, scopeObject, discoverQuery);
long tot = resp.getTotalSearchResults();
for (IndexableObject solrCol : resp.getIndexableObjects()) {
Collection c = ((IndexableCollection) solrCol).getIndexedObject();
collections.add(c);
}
return converter.toRestPage(collections, pageable, tot, utils.obtainProjection());
} catch (SQLException | SearchServiceException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}

View File

@@ -9,6 +9,7 @@ package org.dspace.app.rest.repository;
import java.io.IOException;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import javax.servlet.ServletInputStream;
@@ -17,7 +18,6 @@ import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.logging.log4j.Logger;
import org.dspace.app.rest.Parameter;
import org.dspace.app.rest.SearchRestMethod;
import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
@@ -27,11 +27,18 @@ import org.dspace.app.rest.model.CommunityRest;
import org.dspace.app.rest.model.patch.Patch;
import org.dspace.app.rest.utils.CommunityRestEqualityUtils;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Bitstream;
import org.dspace.content.Community;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.CommunityService;
import org.dspace.core.Context;
import org.dspace.discovery.DiscoverQuery;
import org.dspace.discovery.DiscoverResult;
import org.dspace.discovery.IndexableObject;
import org.dspace.discovery.SearchService;
import org.dspace.discovery.SearchServiceException;
import org.dspace.discovery.indexobject.IndexableCommunity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -59,6 +66,11 @@ public class CommunityRestRepository extends DSpaceObjectRestRepository<Communit
CommunityRestEqualityUtils communityRestEqualityUtils;
@Autowired
SearchService searchService;
@Autowired
AuthorizeService authorizeService;
private CommunityService cs;
public CommunityRestRepository(CommunityService dsoService) {
@@ -147,11 +159,28 @@ public class CommunityRestRepository extends DSpaceObjectRestRepository<Communit
@Override
public Page<CommunityRest> findAll(Context context, Pageable pageable) {
try {
long total = cs.countTotal(context);
List<Community> communities = cs.findAll(context, pageable.getPageSize(),
if (authorizeService.isAdmin(context)) {
long total = cs.countTotal(context);
List<Community> communities = cs.findAll(context, pageable.getPageSize(),
Math.toIntExact(pageable.getOffset()));
return converter.toRestPage(communities, pageable, total, utils.obtainProjection());
} catch (SQLException e) {
return converter.toRestPage(communities, pageable, total, utils.obtainProjection());
} else {
List<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);
}
}
@@ -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
@PreAuthorize("hasPermission(#id, 'COMMUNITY', 'WRITE')")
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;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
@@ -18,6 +19,13 @@ import org.dspace.app.rest.projection.Projection;
import org.dspace.content.Community;
import org.dspace.content.service.CommunityService;
import org.dspace.core.Context;
import org.dspace.discovery.DiscoverQuery;
import org.dspace.discovery.DiscoverResult;
import org.dspace.discovery.IndexableObject;
import org.dspace.discovery.SearchService;
import org.dspace.discovery.SearchServiceException;
import org.dspace.discovery.indexobject.IndexableCommunity;
import org.dspace.discovery.indexobject.factory.IndexObjectFactoryFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -35,6 +43,9 @@ public class CommunitySubcommunityLinkRepository extends AbstractDSpaceRestRepos
@Autowired
CommunityService communityService;
@Autowired
SearchService searchService;
@PreAuthorize("hasPermission(#communityId, 'COMMUNITY', 'READ')")
public Page<CommunityRest> getSubcommunities(@Nullable HttpServletRequest request,
UUID communityId,
@@ -46,10 +57,25 @@ public class CommunitySubcommunityLinkRepository extends AbstractDSpaceRestRepos
if (community == null) {
throw new ResourceNotFoundException("No such community: " + communityId);
}
List<Community> subcommunities = community.getSubcommunities();
return converter.toRestPage(utils.getPage(subcommunities, optionalPageable), projection);
} catch (SQLException e) {
throw new RuntimeException(e);
Pageable pageable = utils.getPageable(optionalPageable);
List<Community> publicSubcommunities = new LinkedList<Community>();
IndexObjectFactoryFactory indexObjectFactory = IndexObjectFactoryFactory.getInstance();
IndexableObject scopeObject = indexObjectFactory.getIndexableObjects(context, community).get(0);
DiscoverQuery discoverQuery = new DiscoverQuery();
discoverQuery.setQuery("*:*");
discoverQuery.setDSpaceObjectFilter(IndexableCommunity.TYPE);
discoverQuery.addFilterQueries("location.parent:" + communityId);
discoverQuery.setStart(Math.toIntExact(pageable.getOffset()));
discoverQuery.setMaxResults(pageable.getPageSize());
DiscoverResult resp = searchService.search(context, scopeObject, discoverQuery);
long tot = resp.getTotalSearchResults();
for (IndexableObject solrCommunities : resp.getIndexableObjects()) {
Community c = ((IndexableCommunity) solrCommunities).getIndexedObject();
publicSubcommunities.add(c);
}
return converter.toRestPage(publicSubcommunities, pageable, tot, utils.obtainProjection());
} catch (SQLException | SearchServiceException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}

View File

@@ -29,7 +29,7 @@ import org.springframework.data.rest.webmvc.ResourceNotFoundException;
* @param <R> the corresponding 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;
@@ -64,4 +64,14 @@ public abstract class DSpaceObjectRestRepository<M extends DSpaceObject, R exten
resourcePatch.patch(obtainContext(), dso, patch.getOperations());
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.Optional;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
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
*/
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!");
}

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.util.List;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dspace.app.rest.Parameter;
import org.dspace.app.rest.SearchRestMethod;
import org.dspace.app.rest.exception.MissingParameterException;

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.SubmissionStepConfig;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Item;
import org.dspace.content.service.BitstreamFormatService;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.ItemService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.EPersonServiceImpl;
@@ -56,6 +59,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
@@ -97,6 +101,9 @@ public class WorkflowItemRestRepository extends DSpaceRestRepository<WorkflowIte
@Autowired
WorkflowService<XmlWorkflowItem> wfs;
@Autowired
AuthorizeService authorizeService;
@Autowired
ClaimedTaskService claimedTaskService;
@@ -188,6 +195,7 @@ public class WorkflowItemRestRepository extends DSpaceRestRepository<WorkflowIte
@Override
public WorkflowItemRest upload(HttpServletRequest request, String apiCategory, String model, Integer id,
MultipartFile file) throws SQLException {
Context context = obtainContext();
WorkflowItemRest wsi = findOne(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);
}
}
/**
* 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.SubmissionStepConfig;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Collection;
import org.dspace.content.Item;
import org.dspace.content.WorkspaceItem;
import org.dspace.content.service.BitstreamFormatService;
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.Pageable;
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.web.multipart.MultipartFile;
@@ -74,7 +78,8 @@ import org.springframework.web.multipart.MultipartFile;
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
@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";
@@ -110,6 +115,9 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
@Autowired
CollectionService collectionService;
@Autowired
AuthorizeService authorizeService;
@Autowired
private UriListHandlerService uriListHandlerService;
@@ -119,7 +127,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
submissionConfigReader = new SubmissionConfigReader();
}
//TODO @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'READ')")
@PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'READ')")
@Override
public WorkspaceItemRest findOne(Context context, Integer id) {
WorkspaceItem witem = null;
@@ -134,7 +142,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
return converter.toRest(witem, utils.obtainProjection());
}
//TODO @PreAuthorize("hasAuthority('ADMIN')")
@PreAuthorize("hasAuthority('ADMIN')")
@Override
public Page<WorkspaceItemRest> findAll(Context context, Pageable pageable) {
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")
public Page<WorkspaceItemRest> findBySubmitter(@Parameter(value = "uuid", required = true) UUID submitterID,
Pageable pageable) {
@@ -213,7 +221,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
return WorkspaceItemRest.class;
}
//TODO @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'WRITE')")
@PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'WRITE')")
@Override
public WorkspaceItemRest upload(HttpServletRequest request, String apiCategory, String model, Integer id,
MultipartFile file) throws SQLException {
@@ -263,7 +271,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
return wsi;
}
//TODO @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'WRITE')")
@PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'WRITE')")
@Override
public void patch(Context context, HttpServletRequest request, String apiCategory, String model, Integer id,
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
protected void delete(Context context, Integer id) throws AuthorizeException {
WorkspaceItem witem = null;
@@ -496,5 +504,41 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
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.authorize.ResourcePolicy;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

View File

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

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

View File

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

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);
if (authentication != null) {
SecurityContextHolder.getContext().setAuthentication(authentication);
restAuthenticationService.invalidateAuthenticationCookie(res);
}
chain.doFilter(req, res);

View File

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

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),
LogoutFilter.class)
//Add a filter before our shibboleth endpoints to do the authentication based on the data in the
// HTTP request
.addFilterBefore(new ShibbolethAuthenticationFilter("/api/authn/shibboleth", authenticationManager(),
restAuthenticationService),
LogoutFilter.class)
// Add a custom Token based authentication filter based on the token previously given to the client
// before each URL
.addFilterBefore(new StatelessAuthenticationFilter(authenticationManager(), restAuthenticationService,

View File

@@ -0,0 +1,91 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.security;
import java.io.Serializable;
import java.sql.SQLException;
import org.apache.commons.lang3.StringUtils;
import org.dspace.app.rest.model.WorkspaceItemRest;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.content.WorkspaceItem;
import org.dspace.content.service.WorkspaceItemService;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.services.RequestService;
import org.dspace.services.model.Request;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
/**
* {@link RestPermissionEvaluatorPlugin} class that evaluate READ, WRITE and DELETE permissions over a WorkspaceItem
*
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
*/
@Component
public class WorkspaceItemRestPermissionEvaluatorPlugin extends RestObjectPermissionEvaluatorPlugin {
private static final Logger log = LoggerFactory.getLogger(WorkspaceItemRestPermissionEvaluatorPlugin.class);
@Autowired
private RequestService requestService;
@Autowired
WorkspaceItemService wis;
@Override
public boolean hasDSpacePermission(Authentication authentication, Serializable targetId, String targetType,
DSpaceRestPermission permission) {
DSpaceRestPermission restPermission = DSpaceRestPermission.convert(permission);
if (!DSpaceRestPermission.READ.equals(restPermission)
&& !DSpaceRestPermission.WRITE.equals(restPermission)
&& !DSpaceRestPermission.DELETE.equals(restPermission)) {
return false;
}
if (!StringUtils.equalsIgnoreCase(targetType, WorkspaceItemRest.NAME)) {
return false;
}
Request request = requestService.getCurrentRequest();
Context context = ContextUtil.obtainContext(request.getServletRequest());
EPerson ePerson = null;
WorkspaceItem witem = null;
try {
ePerson = context.getCurrentUser();
Integer dsoId = Integer.parseInt(targetId.toString());
// anonymous user
if (ePerson == null) {
return false;
}
witem = wis.find(context, dsoId);
// If the dso is null then we give permission so we can throw another status
// code instead
if (witem == null) {
return true;
}
if (witem.getSubmitter() != null) {
if (witem.getSubmitter().getID().equals(ePerson.getID())) {
return true;
}
}
} catch (SQLException e) {
log.error(e.getMessage(), e);
}
return false;
}
}

View File

@@ -12,6 +12,7 @@ import java.sql.SQLException;
import java.text.ParseException;
import java.util.Iterator;
import java.util.List;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -37,11 +38,13 @@ import org.springframework.stereotype.Component;
*
* @author Frederic Van Reet (frederic dot vanreet at atmire dot com)
* @author Tom Desair (tom dot desair at atmire dot com)
* @author Giuseppe Digilio (giuseppe dot digilio at 4science dot it)
*/
@Component
public class JWTTokenRestAuthenticationServiceImpl implements RestAuthenticationService, InitializingBean {
private static final Logger log = LoggerFactory.getLogger(RestAuthenticationService.class);
private static final String AUTHORIZATION_COOKIE = "Authorization-cookie";
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String AUTHORIZATION_TYPE = "Bearer";
@@ -61,7 +64,7 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
@Override
public void addAuthenticationDataForUser(HttpServletRequest request, HttpServletResponse response,
DSpaceAuthentication authentication) throws IOException {
DSpaceAuthentication authentication, boolean addCookie) throws IOException {
try {
Context context = ContextUtil.obtainContext(request);
context.setCurrentUser(ePersonService.findByEmail(context, authentication.getName()));
@@ -71,7 +74,7 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
String token = jwtTokenHandler.createTokenForEPerson(context, request,
authentication.getPreviousLoginDate(), groups);
addTokenToResponse(response, token);
addTokenToResponse(response, token, addCookie);
context.commit();
} catch (JOSEException e) {
@@ -99,15 +102,26 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
@Override
public boolean hasAuthenticationData(HttpServletRequest request) {
return StringUtils.isNotBlank(request.getHeader(AUTHORIZATION_HEADER));
return StringUtils.isNotBlank(request.getHeader(AUTHORIZATION_HEADER))
|| StringUtils.isNotBlank(getAuthorizationCookie(request));
}
@Override
public void invalidateAuthenticationData(HttpServletRequest request, Context context) throws Exception {
public void invalidateAuthenticationData(HttpServletRequest request, HttpServletResponse response,
Context context) throws Exception {
String token = getToken(request);
invalidateAuthenticationCookie(response);
jwtTokenHandler.invalidateToken(token, request, context);
}
@Override
public void invalidateAuthenticationCookie(HttpServletResponse response) {
Cookie cookie = new Cookie(AUTHORIZATION_COOKIE, "");
cookie.setHttpOnly(true);
cookie.setMaxAge(0);
response.addCookie(cookie);
}
@Override
public AuthenticationService getAuthenticationService() {
return authenticationService;
@@ -140,18 +154,42 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
return wwwAuthenticate.toString();
}
private void addTokenToResponse(final HttpServletResponse response, final String token) throws IOException {
private void addTokenToResponse(final HttpServletResponse response, final String token, final Boolean addCookie)
throws IOException {
// we need authentication cookies because Shibboleth can't use the authentication headers due to the redirects
if (addCookie) {
Cookie cookie = new Cookie(AUTHORIZATION_COOKIE, token);
cookie.setHttpOnly(true);
cookie.setSecure(true);
response.addCookie(cookie);
}
response.setHeader(AUTHORIZATION_HEADER, String.format("%s %s", AUTHORIZATION_TYPE, token));
}
private String getToken(HttpServletRequest request) {
String tokenValue = null;
String authHeader = request.getHeader(AUTHORIZATION_HEADER);
String authCookie = getAuthorizationCookie(request);
if (StringUtils.isNotBlank(authHeader)) {
String tokenValue = authHeader.replace(AUTHORIZATION_TYPE, "").trim();
return tokenValue;
} else {
return null;
tokenValue = authHeader.replace(AUTHORIZATION_TYPE, "").trim();
} else if (StringUtils.isNotBlank(authCookie)) {
tokenValue = authCookie;
}
return tokenValue;
}
private String getAuthorizationCookie(HttpServletRequest request) {
String authCookie = "";
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(AUTHORIZATION_COOKIE) && StringUtils.isNotEmpty(cookie.getValue())) {
authCookie = cookie.getValue();
}
}
}
return authCookie;
}
}

View File

@@ -8,7 +8,6 @@
package org.dspace.app.rest.submit;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@@ -141,7 +140,7 @@ public class SubmissionService {
* Build the rest representation of a bitstream as used in the upload section
* ({@link DataUpload}. It contains all its metadata and the list of applied
* access conditions (@link {@link UploadBitstreamAccessConditionDTO}
*
*
* @param configurationService the DSpace ConfigurationService
* @param source the bitstream to translate in its rest submission
* representation

View File

@@ -8,7 +8,6 @@
package org.dspace.app.rest.submit.factory.impl;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

View File

@@ -28,7 +28,7 @@ public class RegexUtils {
* identifier (digits or uuid)
*/
public static final String REGEX_REQUESTMAPPING_IDENTIFIER_AS_STRING_VERSION_STRONG = "/{id:^(?!^\\d+$)" +
"(?!^[0-9a-fxA-FX]{8}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{12}$)[\\w+\\-]+$+}";
"(?!^[0-9a-fxA-FX]{8}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{12}$)[\\w+\\-\\.]+$+}";
/**
* Regular expression in the request mapping to accept number as identifier

View File

@@ -21,6 +21,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.SQLException;
@@ -55,6 +56,7 @@ import org.dspace.app.rest.model.ProcessRest;
import org.dspace.app.rest.model.ResourcePolicyRest;
import org.dspace.app.rest.model.RestAddressableModel;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.VersionHistoryRest;
import org.dspace.app.rest.model.hateoas.DSpaceResource;
import org.dspace.app.rest.model.hateoas.EmbeddedPage;
import org.dspace.app.rest.model.hateoas.HALResource;
@@ -64,18 +66,22 @@ import org.dspace.app.rest.projection.EmbedRelsProjection;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.repository.DSpaceRestRepository;
import org.dspace.app.rest.repository.LinkRestRepository;
import org.dspace.app.rest.repository.ReloadableEntityObjectRepository;
import org.dspace.content.BitstreamFormat;
import org.dspace.content.DSpaceObject;
import org.dspace.content.service.BitstreamFormatService;
import org.dspace.content.service.DSpaceObjectService;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Context;
import org.dspace.services.ConfigurationService;
import org.dspace.services.RequestService;
import org.dspace.util.UUIDUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
@@ -110,6 +116,10 @@ public class Utils {
@Autowired
RequestService requestService;
@Autowired
@Qualifier("defaultConversionService")
ConversionService conversionService;
@Autowired(required = true)
private List<DSpaceObjectService<? extends DSpaceObject>> dSpaceObjectServices;
@@ -119,6 +129,9 @@ public class Utils {
@Autowired
private ConverterService converter;
@Autowired
private ConfigurationService configurationService;
/** Cache to support fast lookups of LinkRest method annotation information. */
private Map<Method, Optional<LinkRest>> linkAnnotationForMethod = new HashMap<>();
@@ -168,12 +181,32 @@ public class Utils {
.withRel(rel);
}
/**
* Retrieve the {@link DSpaceRestRepository} for the specified category and model in the plural form as used in the endpoints.
* If the model is available in its singular form use {@link #getResourceRepositoryByCategoryAndModel(String, String)}
*
* @param apiCategory
* @param modelPlural
* @return
*/
public DSpaceRestRepository getResourceRepository(String apiCategory, String modelPlural) {
String model = makeSingular(modelPlural);
return getResourceRepositoryByCategoryAndModel(apiCategory, model);
}
/**
* Retrieve the {@link DSpaceRestRepository} for the specified category and model. The model is in the singular form
* as returned by the {@link RestAddressableModel#getType()} method
*
* @param apiCategory
* @param modelSingular
* @return
*/
public DSpaceRestRepository getResourceRepositoryByCategoryAndModel(String apiCategory, String modelSingular) {
try {
return applicationContext.getBean(apiCategory + "." + model, DSpaceRestRepository.class);
return applicationContext.getBean(apiCategory + "." + modelSingular, DSpaceRestRepository.class);
} catch (NoSuchBeanDefinitionException e) {
throw new RepositoryNotFoundException(apiCategory, model);
throw new RepositoryNotFoundException(apiCategory, modelSingular);
}
}
@@ -196,6 +229,9 @@ public class Utils {
if (StringUtils.equals(modelPlural, "processes")) {
return ProcessRest.NAME;
}
if (StringUtils.equals(modelPlural, "versionhistories")) {
return VersionHistoryRest.NAME;
}
return modelPlural.replaceAll("s$", "");
}
@@ -784,4 +820,89 @@ public class Utils {
}
return contentId;
}
/**
* Convert the input string in the primary key class according to the repository interface
*
* @param repository
* @param pkStr
* @return
*/
public Serializable castToPKClass(ReloadableEntityObjectRepository repository, String pkStr) {
return (Serializable) conversionService.convert(pkStr, repository.getPKClass());
}
/**
* Return the dspace api model object corresponding to the provided, not null, rest object. This only works when the
* rest object is supported by a {@link DSpaceRestRepository} that also implement the
* {@link ReloadableEntityObjectRepository} interface. If this is not the case the method will throw an
* IllegalArgumentException
*
* @param context
* the DSpace Context
* @param restObj
* the not null rest object. If null the method will throws an {@link IllegalArgumentException}
* @return the dspace api model object corresponding to the provided, not null, rest object
* @throws IllegalArgumentException
* if the restObj is not supported by a {@link DSpaceRestRepository} that also implement the
* {@link ReloadableEntityObjectRepository} interface
* @throws SQLException
* if a database error occur
*/
public Object getDSpaceAPIObjectFromRest(Context context, BaseObjectRest restObj)
throws IllegalArgumentException, SQLException {
DSpaceRestRepository repository = getResourceRepositoryByCategoryAndModel(restObj.getCategory(),
restObj.getType());
Serializable pk = castToPKClass((ReloadableEntityObjectRepository) repository, restObj.getId().toString());
return ((ReloadableEntityObjectRepository) repository).findDomainObjectByPk(context, pk);
}
/**
* Get the rest object associated with the specified URI
*
* @param context the DSpace context
* @param uri the uri of a {@link BaseObjectRest}
* @return the {@link BaseObjectRest} identified by the provided uri
* @throws SQLException if a database error occur
* @throws IllegalArgumentException if the uri is not valid
*/
public BaseObjectRest getBaseObjectRestFromUri(Context context, String uri) throws SQLException {
String dspaceUrl = configurationService.getProperty("dspace.server.url");
// first check if the uri could be valid
if (!StringUtils.startsWith(uri, dspaceUrl)) {
throw new IllegalArgumentException("the supplied uri is not valid: " + uri);
}
// extract from the uri the category, model and id components
// they start after the dspaceUrl/api/{apiCategory}/{apiModel}/{id}
String[] uriParts = uri.substring(dspaceUrl.length() + (dspaceUrl.endsWith("/") ? 0 : 1) + "api/".length())
.split("/", 3);
if (uriParts.length != 3) {
throw new IllegalArgumentException("the supplied uri is not valid: " + uri);
}
DSpaceRestRepository repository;
try {
repository = getResourceRepository(uriParts[0], uriParts[1]);
if (!(repository instanceof ReloadableEntityObjectRepository)) {
throw new IllegalArgumentException("the supplied uri is not valid: " + uri);
}
} catch (RepositoryNotFoundException e) {
throw new IllegalArgumentException("the supplied uri is not valid: " + uri, e);
}
Serializable pk;
try {
// cast the string id in the uriParts to the real pk class
pk = castToPKClass((ReloadableEntityObjectRepository) repository, uriParts[2]);
} catch (Exception e) {
throw new IllegalArgumentException("the supplied uri is not valid: " + uri, e);
}
try {
// disable the security as we only need to retrieve the object to further process the authorization
context.turnOffAuthorisationSystem();
return (BaseObjectRest) repository.findOne(context, pk);
} finally {
context.restoreAuthSystemState();
}
}
}

View File

@@ -28,7 +28,6 @@ import com.lyncode.xoai.dataprovider.services.api.ResourceResolver;
import com.lyncode.xoai.dataprovider.services.impl.BaseDateProvider;
import com.lyncode.xoai.dataprovider.xml.xoaiconfig.Configuration;
import com.lyncode.xoai.dataprovider.xml.xoaiconfig.ContextConfiguration;
import org.apache.commons.lang3.time.DateUtils;
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
@@ -39,7 +38,6 @@ import org.dspace.xoai.services.api.EarliestDateResolver;
import org.dspace.xoai.services.api.cache.XOAICacheService;
import org.dspace.xoai.services.api.config.XOAIManagerResolver;
import org.dspace.xoai.services.api.xoai.DSpaceFilterResolver;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;

View File

@@ -12,10 +12,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.junit.Before;
import org.junit.Test;

View File

@@ -15,13 +15,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.builder.ItemBuilder;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.junit.Before;

View File

@@ -21,6 +21,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.Base64;
import javax.servlet.http.Cookie;
import org.dspace.app.rest.builder.GroupBuilder;
import org.dspace.app.rest.matcher.AuthenticationStatusMatcher;
@@ -40,6 +41,7 @@ import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
*
* @author Frederic Van Reet (frederic dot vanreet at atmire dot com)
* @author Tom Desair (tom dot desair at atmire dot com)
* @author Giuseppe Digilio (giuseppe dot digilio at 4science dot it)
*/
public class AuthenticationRestControllerIT extends AbstractControllerIntegrationTest {
@@ -48,6 +50,9 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
public static final String[] PASS_ONLY = {"org.dspace.authenticate.PasswordAuthentication"};
public static final String[] SHIB_ONLY = {"org.dspace.authenticate.ShibAuthentication"};
public static final String[] SHIB_AND_PASS =
{"org.dspace.authenticate.ShibAuthentication",
"org.dspace.authenticate.PasswordAuthentication"};
public static final String[] SHIB_AND_IP =
{"org.dspace.authenticate.IPAuthentication",
"org.dspace.authenticate.ShibAuthentication"};
@@ -97,7 +102,43 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(false)))
.andExpect(jsonPath("$.type", is("status")));
.andExpect(jsonPath("$.type", is("status")))
.andExpect(header().string("WWW-Authenticate",
"password realm=\"DSpace REST API\""));
}
@Test
public void testStatusAuthenticatedWithCookie() throws Exception {
context.turnOffAuthorisationSystem();
//Enable Shibboleth login
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
context.restoreAuthSystemState();
//Simulate that a shibboleth authentication has happened
String token = getClient().perform(post("/api/authn/login")
.requestAttr("SHIB-MAIL", eperson.getEmail())
.requestAttr("SHIB-SCOPED-AFFILIATION", "faculty;staff"))
.andExpect(status().isOk())
.andReturn().getResponse().getHeader(AUTHORIZATION_HEADER).replace("Bearer ", "");
Cookie[] cookies = new Cookie[1];
cookies[0] = new Cookie(AUTHORIZATION_COOKIE, token);
//Check if we are authenticated with a status request with authorization cookie
getClient().perform(get("/api/authn/status")
.secure(true)
.cookie(cookies))
.andExpect(status().isOk())
//We expect the content type to be "application/hal+json;charset=UTF-8"
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(true)))
.andExpect(jsonPath("$.type", is("status")));
//Logout
getClient(token).perform(get("/api/authn/logout"))
.andExpect(status().isNoContent());
}
@Test
@@ -354,6 +395,108 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(status().isMethodNotAllowed());
}
@Test
public void testShibbolethLoginURLWithDefaultLazyURL() throws Exception {
context.turnOffAuthorisationSystem();
//Enable Shibboleth login
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
//Create a reviewers group
Group reviewersGroup = GroupBuilder.createGroup(context)
.withName("Reviewers")
.build();
//Faculty members are assigned to the Reviewers group
configurationService.setProperty("authentication-shibboleth.role.faculty", "Reviewers");
context.restoreAuthSystemState();
getClient().perform(post("/api/authn/login").header("Referer", "http://my.uni.edu"))
.andExpect(status().isUnauthorized())
.andExpect(header().string("WWW-Authenticate",
"shibboleth realm=\"DSpace REST API\", " +
"location=\"https://localhost/Shibboleth.sso/Login?" +
"target=http%3A%2F%2Flocalhost%2Fapi%2Fauthn%2Fshibboleth%3F" +
"redirectUrl%3Dhttp%3A%2F%2Fmy.uni.edu\""));
}
@Test
public void testShibbolethLoginURLWithServerlURLConteiningPort() throws Exception {
context.turnOffAuthorisationSystem();
//Enable Shibboleth login
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
configurationService.setProperty("dspace.server.url", "http://localhost:8080/server");
configurationService.setProperty("authentication-shibboleth.lazysession.secure", false);
//Create a reviewers group
Group reviewersGroup = GroupBuilder.createGroup(context)
.withName("Reviewers")
.build();
//Faculty members are assigned to the Reviewers group
configurationService.setProperty("authentication-shibboleth.role.faculty", "Reviewers");
context.restoreAuthSystemState();
getClient().perform(post("/api/authn/login").header("Referer", "http://my.uni.edu"))
.andExpect(status().isUnauthorized())
.andExpect(header().string("WWW-Authenticate",
"shibboleth realm=\"DSpace REST API\", " +
"location=\"http://localhost:8080/Shibboleth.sso/Login?" +
"target=http%3A%2F%2Flocalhost%3A8080%2Fserver%2Fapi%2Fauthn%2Fshibboleth%3F" +
"redirectUrl%3Dhttp%3A%2F%2Fmy.uni.edu\""));
}
@Test
public void testShibbolethLoginURLWithConfiguredLazyURL() throws Exception {
context.turnOffAuthorisationSystem();
//Enable Shibboleth login
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
configurationService.setProperty("authentication-shibboleth.lazysession.loginurl",
"http://shibboleth.org/Shibboleth.sso/Login");
//Create a reviewers group
Group reviewersGroup = GroupBuilder.createGroup(context)
.withName("Reviewers")
.build();
//Faculty members are assigned to the Reviewers group
configurationService.setProperty("authentication-shibboleth.role.faculty", "Reviewers");
context.restoreAuthSystemState();
getClient().perform(post("/api/authn/login").header("Referer", "http://my.uni.edu"))
.andExpect(status().isUnauthorized())
.andExpect(header().string("WWW-Authenticate",
"shibboleth realm=\"DSpace REST API\", " +
"location=\"http://shibboleth.org/Shibboleth.sso/Login?" +
"target=http%3A%2F%2Flocalhost%2Fapi%2Fauthn%2Fshibboleth%3F" +
"redirectUrl%3Dhttp%3A%2F%2Fmy.uni.edu\""));
}
@Test
public void testShibbolethLoginURLWithConfiguredLazyURLWithPort() throws Exception {
context.turnOffAuthorisationSystem();
//Enable Shibboleth login
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
configurationService.setProperty("authentication-shibboleth.lazysession.loginurl",
"http://shibboleth.org:8080/Shibboleth.sso/Login");
//Create a reviewers group
Group reviewersGroup = GroupBuilder.createGroup(context)
.withName("Reviewers")
.build();
//Faculty members are assigned to the Reviewers group
configurationService.setProperty("authentication-shibboleth.role.faculty", "Reviewers");
context.restoreAuthSystemState();
getClient().perform(post("/api/authn/login").header("Referer", "http://my.uni.edu"))
.andExpect(status().isUnauthorized())
.andExpect(header().string("WWW-Authenticate",
"shibboleth realm=\"DSpace REST API\", " +
"location=\"http://shibboleth.org:8080/Shibboleth.sso/Login?" +
"target=http%3A%2F%2Flocalhost%2Fapi%2Fauthn%2Fshibboleth%3F" +
"redirectUrl%3Dhttp%3A%2F%2Fmy.uni.edu\""));
}
@Test
@Ignore
// Ignored until an endpoint is added to return all groups
@@ -375,7 +518,9 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(status().isUnauthorized())
.andExpect(header().string("WWW-Authenticate",
"shibboleth realm=\"DSpace REST API\", " +
"location=\"/Shibboleth.sso/Login?target=http%3A%2F%2Fmy.uni.edu\""));
"location=\"https://localhost/Shibboleth.sso/Login?" +
"target=http%3A%2F%2Flocalhost%2Fapi%2Fauthn%2Fshibboleth%3F" +
"redirectUrl%3Dhttp%3A%2F%2Fmy.uni.edu\""));
//Simulate that a shibboleth authentication has happened
@@ -411,7 +556,9 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
.andExpect(status().isUnauthorized())
.andExpect(header().string("WWW-Authenticate",
"ip realm=\"DSpace REST API\", shibboleth realm=\"DSpace REST API\", " +
"location=\"/Shibboleth.sso/Login?target=http%3A%2F%2Fmy.uni.edu\""));
"location=\"https://localhost/Shibboleth.sso/Login?" +
"target=http%3A%2F%2Flocalhost%2Fapi%2Fauthn%2Fshibboleth%3F" +
"redirectUrl%3Dhttp%3A%2F%2Fmy.uni.edu\""));
//Simulate that a shibboleth authentication has happened
String token = getClient().perform(post("/api/authn/login")
@@ -454,4 +601,164 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
EPersonMatcher.matchEPersonWithGroups(eperson.getEmail(), "Anonymous")));
}
@Test
public void testShibbolethAndPasswordAuthentication() throws Exception {
context.turnOffAuthorisationSystem();
//Enable Shibboleth and password login
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_AND_PASS);
context.restoreAuthSystemState();
//Check if WWW-Authenticate header contains shibboleth and password
getClient().perform(get("/api/authn/status").header("Referer", "http://my.uni.edu"))
.andExpect(status().isOk())
.andExpect(header().string("WWW-Authenticate",
"shibboleth realm=\"DSpace REST API\", " +
"location=\"https://localhost/Shibboleth.sso/Login?" +
"target=http%3A%2F%2Flocalhost%2Fapi%2Fauthn%2Fshibboleth%3F" +
"redirectUrl%3Dhttp%3A%2F%2Fmy.uni.edu\"" +
", password realm=\"DSpace REST API\""));
//Simulate a password authentication
String token = getAuthToken(eperson.getEmail(), password);
//Check if we have a valid token
getClient(token).perform(get("/api/authn/status"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(true)))
.andExpect(jsonPath("$.type", is("status")));
//Logout
getClient(token).perform(get("/api/authn/logout"))
.andExpect(status().isNoContent());
//Check if we are actually logged out
getClient(token).perform(get("/api/authn/status"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(false)))
.andExpect(jsonPath("$.type", is("status")));
//Simulate that a shibboleth authentication has happened
token = getClient().perform(post("/api/authn/login")
.requestAttr("SHIB-MAIL", eperson.getEmail())
.requestAttr("SHIB-SCOPED-AFFILIATION", "faculty;staff"))
.andExpect(status().isOk())
.andReturn().getResponse().getHeader(AUTHORIZATION_HEADER).replace("Bearer ", "");
//Check if we have a valid token
getClient(token).perform(get("/api/authn/status"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(true)))
.andExpect(jsonPath("$.type", is("status")));
//Logout
getClient(token).perform(get("/api/authn/logout"))
.andExpect(status().isNoContent());
}
@Test
public void testOnlyPasswordAuthenticationWorks() throws Exception {
context.turnOffAuthorisationSystem();
//Enable only password login
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", PASS_ONLY);
context.restoreAuthSystemState();
//Check if WWW-Authenticate header contains only
getClient().perform(get("/api/authn/status").header("Referer", "http://my.uni.edu"))
.andExpect(status().isOk())
.andExpect(header().string("WWW-Authenticate",
"password realm=\"DSpace REST API\""));
//Simulate a password authentication
String token = getAuthToken(eperson.getEmail(), password);
//Check if we have a valid token
getClient(token).perform(get("/api/authn/status"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.okay", is(true)))
.andExpect(jsonPath("$.authenticated", is(true)))
.andExpect(jsonPath("$.type", is("status")));
//Logout
getClient(token).perform(get("/api/authn/logout"))
.andExpect(status().isNoContent());
}
@Test
public void testShibbolethAuthenticationDoesNotWorkWithPassOnly() throws Exception {
context.turnOffAuthorisationSystem();
//Enable only password login
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", PASS_ONLY);
context.restoreAuthSystemState();
//Check if WWW-Authenticate header contains only password
getClient().perform(get("/api/authn/status").header("Referer", "http://my.uni.edu"))
.andExpect(status().isOk())
.andExpect(header().string("WWW-Authenticate",
"password realm=\"DSpace REST API\""));
//Check if a shibboleth authentication fails
getClient().perform(post("/api/authn/login")
.requestAttr("SHIB-MAIL", eperson.getEmail())
.requestAttr("SHIB-SCOPED-AFFILIATION", "faculty;staff"))
.andExpect(status().isUnauthorized());
}
@Test
public void testOnlyShibbolethAuthenticationWorks() throws Exception {
context.turnOffAuthorisationSystem();
//Enable only Shibboleth login
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
context.restoreAuthSystemState();
//Check if WWW-Authenticate header contains only shibboleth
getClient().perform(get("/api/authn/status").header("Referer", "http://my.uni.edu"))
.andExpect(status().isOk())
.andExpect(header().string("WWW-Authenticate",
"shibboleth realm=\"DSpace REST API\", " +
"location=\"https://localhost/Shibboleth.sso/Login?" +
"target=http%3A%2F%2Flocalhost%2Fapi%2Fauthn%2Fshibboleth%3F" +
"redirectUrl%3Dhttp%3A%2F%2Fmy.uni.edu\""));
//Simulate that a shibboleth authentication has happened
String token = getClient().perform(post("/api/authn/login")
.requestAttr("SHIB-MAIL", eperson.getEmail())
.requestAttr("SHIB-SCOPED-AFFILIATION", "faculty;staff"))
.andExpect(status().isOk())
.andReturn().getResponse().getHeader(AUTHORIZATION_HEADER);
//Logout
getClient(token).perform(get("/api/authn/logout"))
.andExpect(status().isNoContent());
}
@Test
public void testPasswordAuthenticationDoesNotWorkWithShibOnly() throws Exception {
context.turnOffAuthorisationSystem();
//Enable only Shibboleth login
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
//Create a reviewers group
Group reviewersGroup = GroupBuilder.createGroup(context)
.withName("Reviewers")
.build();
//Faculty members are assigned to the Reviewers group
configurationService.setProperty("authentication-shibboleth.role.faculty", "Reviewers");
context.restoreAuthSystemState();
getClient().perform(post("/api/authn/login")
.param("user", eperson.getEmail())
.param("password", password))
.andExpect(status().isUnauthorized());
}
}

View File

@@ -0,0 +1,173 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest;
import static com.jayway.jsonpath.JsonPath.read;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.fail;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import com.jayway.jsonpath.matchers.JsonPathMatchers;
import org.dspace.app.rest.authorization.AlwaysTrueFeature;
import org.dspace.app.rest.authorization.AuthorizationFeature;
import org.dspace.app.rest.authorization.AuthorizationFeatureService;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Test suite for the Authorization Feature endpoint
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
public class AuthorizationFeatureRestRepositoryIT extends AbstractControllerIntegrationTest {
@Autowired
private AuthorizationFeatureService authzFeatureService;
@Test
/**
* All the features should be returned
*
* @throws Exception
*/
public void findAllTest() throws Exception {
int featuresNum = authzFeatureService.findAll().size();
int expReturn = featuresNum > 20 ? 20 : featuresNum;
String adminToken = getAuthToken(admin.getEmail(), password);
// verify that only the admin can access the endpoint (see subsequent call in the method)
getClient(adminToken).perform(get("/api/authz/features")).andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.features", Matchers.hasSize(is(expReturn))))
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/authz/features")))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(featuresNum)));
// verify that anonymous user cannot access
getClient().perform(get("/api/authz/features")).andExpect(status().isUnauthorized());
// verify that normal user cannot access
String epersonAuthToken = getAuthToken(eperson.getEmail(), password);
getClient(epersonAuthToken).perform(get("/api/authz/features")).andExpect(status().isForbidden());
}
@Test
/**
* The feature endpoint must provide proper pagination. Unauthorized and
* forbidden scenarios are managed in the findAllTest
*
* @throws Exception
*/
public void findAllWithPaginationTest() throws Exception {
int featuresNum = authzFeatureService.findAll().size();
String adminToken = getAuthToken(admin.getEmail(), password);
List<String> featureIDs = new ArrayList<String>();
for (int page = 0; page < featuresNum; page++) {
AtomicReference<String> idRef = new AtomicReference<String>();
getClient(adminToken)
.perform(get("/api/authz/features").param("page", String.valueOf(page)).param("size", "1"))
.andExpect(status().isOk()).andExpect(jsonPath("$._embedded.features", Matchers.hasSize(is(1))))
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/authz/features")))
.andExpect(
(page == 0) ? jsonPath("$._links.prev.href").doesNotExist()
: jsonPath("$._links.prev.href", Matchers.containsString("/api/authz/features")))
.andExpect((page == featuresNum - 1)
? jsonPath("$._links.next.href").doesNotExist()
: jsonPath("$._links.next.href", Matchers.containsString("/api/authz/features")))
.andExpect(jsonPath("$._links.first.href", Matchers.containsString("/api/authz/features")))
.andExpect(jsonPath("$._links.last.href", Matchers.containsString("/api/authz/features")))
.andExpect(jsonPath("$.page.size", is(1)))
.andExpect(jsonPath("$.page.totalElements", is(Integer.valueOf(featuresNum))))
.andDo(result -> idRef
.set(read(result.getResponse().getContentAsString(), "$._embedded.features[0].id")));
if (idRef.get() == null || featureIDs.contains(idRef.get())) {
fail("Duplicate feature " + idRef.get() + " returned at page " + page);
}
featureIDs.add(idRef.get());
}
}
@Test
/**
* The feature resource endpoint must expose the proper structure and be reserved to administrators
*
* @throws Exception
*/
public void findOneTest() throws Exception {
String adminToken = getAuthToken(admin.getEmail(), password);
// verify that only the admin can access the endpoint (see subsequent call in the method)
getClient(adminToken).perform(get("/api/authz/features/withdrawItem")).andExpect(status().isOk())
.andExpect(jsonPath("$.id", is("withdrawItem")))
.andExpect(jsonPath("$.description", Matchers.any(String.class)))
.andExpect(jsonPath("$.resourcetypes", Matchers.contains("core.item")))
.andExpect(jsonPath("$.type", is("feature")));
// verify that anonymous user cannot access
getClient().perform(get("/api/authz/features/withdrawItem")).andExpect(status().isUnauthorized());
// verify that normal user cannot access
String epersonAuthToken = getAuthToken(eperson.getEmail(), password);
getClient(epersonAuthToken).perform(get("/api/authz/features/withdrawItem")).andExpect(status().isForbidden());
}
@Test
public void findOneNotFoundTest() throws Exception {
String adminToken = getAuthToken(admin.getEmail(), password);
// verify that only the admin can access the endpoint and get the not found response code
// (see subsequent calls in the method for unauthorized and forbidden attempts)
getClient(adminToken).perform(get("/api/authz/features/not-existing-feature")).andExpect(status().isNotFound());
// verify that anonymous user cannot access, without information disclosure
getClient().perform(get("/api/authz/features/not-existing-feature")).andExpect(status().isUnauthorized());
// verify that normal user cannot access, without information disclosure
getClient(adminToken).perform(get("/api/authz/features/1")).andExpect(status().isNotFound());
}
@Test
/**
* It should be possible to find features by resourcetype. The endpoint is only available to administrators
*
* @throws Exception
*/
public void findByResourceTypeTest() throws Exception {
AuthorizationFeature alwaysTrueFeature = authzFeatureService.find(AlwaysTrueFeature.NAME);
String adminToken = getAuthToken(admin.getEmail(), password);
for (String type : alwaysTrueFeature.getSupportedTypes()) {
// verify that only the admin can access the endpoint (see subsequent call in the method)
getClient(adminToken).perform(get("/api/authz/features/search/resourcetype").param("type", type))
.andExpect(status().isOk())
.andExpect(jsonPath("$",
JsonPathMatchers.hasJsonPath("$._embedded.features",
Matchers.everyItem(
JsonPathMatchers.hasJsonPath("$.resourcetypes",
Matchers.hasItem(is(type))))
)))
.andExpect(
jsonPath("$._links.self.href",
Matchers.containsString("/api/authz/features/search/resourcetype")));
}
// verify that the right response code is returned also for not existing types
getClient(adminToken).perform(get("/api/authz/features/search/resourcetype").param("type", "NOT-EXISTING"))
.andExpect(status().isOk()).andExpect(jsonPath("$.page.totalElements", is(0)));
// verify that anonymous user cannot access, without information disclosure
getClient().perform(get("/api/authz/features/search/resourcetype").param("type", "core.item"))
.andExpect(status().isUnauthorized());
// verify that normal user cannot access, without information disclosure
String epersonAuthToken = getAuthToken(eperson.getEmail(), password);
getClient(epersonAuthToken).perform(get("/api/authz/features/search/resourcetype").param("type", "core.item"))
.andExpect(status().isForbidden());
}
}

View File

@@ -0,0 +1,173 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.junit.Assert.assertThat;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.ArrayUtils;
import org.dspace.app.rest.authorization.AlwaysFalseFeature;
import org.dspace.app.rest.authorization.AlwaysThrowExceptionFeature;
import org.dspace.app.rest.authorization.AlwaysTrueFeature;
import org.dspace.app.rest.authorization.AuthorizationFeature;
import org.dspace.app.rest.authorization.AuthorizationFeatureService;
import org.dspace.app.rest.authorization.TrueForAdminsFeature;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.model.CollectionRest;
import org.dspace.app.rest.model.SiteRest;
import org.dspace.app.rest.projection.DefaultProjection;
import org.dspace.app.rest.test.AbstractIntegrationTestWithDatabase;
import org.dspace.app.rest.utils.DSpaceConfigurationInitializer;
import org.dspace.app.rest.utils.DSpaceKernelInitializer;
import org.dspace.content.Site;
import org.dspace.content.service.SiteService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
/**
* Test for the Authorization Feature Service
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
//Run tests with JUnit 4 and Spring TestContext Framework
@RunWith(SpringRunner.class)
//Specify main class to use to load Spring ApplicationContext
//NOTE: By default, Spring caches and reuses ApplicationContext for each integration test (to speed up tests)
//See: https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#integration-testing
@SpringBootTest(classes = Application.class)
//Load DSpace initializers in Spring ApplicationContext (to initialize DSpace Kernel & Configuration)
@ContextConfiguration(initializers = { DSpaceKernelInitializer.class, DSpaceConfigurationInitializer.class })
//Tell Spring to make ApplicationContext an instance of WebApplicationContext (for web-based tests)
@WebAppConfiguration
public class AuthorizationFeatureServiceIT extends AbstractIntegrationTestWithDatabase {
@Autowired
private SiteService siteService;
@Autowired
private ConverterService converterService;
@Autowired
private AuthorizationFeatureService authzFeatureService;
@Test
/**
* All the features available in the Sprint Context should be returned
*
* @throws Exception
*/
public void findAllTest() throws Exception {
List<AuthorizationFeature> authzFeatureServiceFindAll = authzFeatureService.findAll();
assertThat("We have at least our 7 mock features for testing",
authzFeatureServiceFindAll.size(), greaterThanOrEqualTo(7));
Set<String> featureNames = new HashSet<String>();
for (AuthorizationFeature f : authzFeatureServiceFindAll) {
featureNames.add(f.getName());
}
assertThat("all the features must have unique name", authzFeatureServiceFindAll.size(),
equalTo(featureNames.size()));
}
@Test
/**
* The find method should return existing feature and null in the case the feature doesn't exist
*
* @throws Exception
*/
public void findTest() throws Exception {
AuthorizationFeature aFeature = authzFeatureService.find(AlwaysTrueFeature.NAME);
assertThat("check that one of our mock feature is retrieved", aFeature.getName(),
equalTo(AlwaysTrueFeature.NAME));
AuthorizationFeature aNotExistingFeature = authzFeatureService.find("this feature doesn't exist!");
assertThat("check that not existing feature name return null", aNotExistingFeature, equalTo(null));
}
@Test
/**
* The findByResourceType must return only features that support the specified type
*
* @throws Exception
*/
public void findByResourceTypeTest() throws Exception {
// we have at least one feature that support the Site object
final String siteUniqueType = SiteRest.CATEGORY + "." + SiteRest.NAME;
List<AuthorizationFeature> siteFeatures = authzFeatureService.findByResourceType(siteUniqueType);
assertThat(siteFeatures.size(), greaterThan(0));
boolean alwaysTrueFound = false;
for (AuthorizationFeature f : siteFeatures) {
assertThat(ArrayUtils.contains(f.getSupportedTypes(), siteUniqueType), equalTo(true));
alwaysTrueFound = alwaysTrueFound || AlwaysTrueFeature.NAME.equals(f.getName());
}
assertThat(alwaysTrueFound, equalTo(true));
// we can check that the AlwaysTrueFeature is returned also when searching for a
// type other than the Site (that is the first type supported by the feature)
alwaysTrueFound = false;
final String collectionUniqueType = CollectionRest.CATEGORY + "." + CollectionRest.NAME;
List<AuthorizationFeature> collectionFeatures = authzFeatureService.findByResourceType(collectionUniqueType);
for (AuthorizationFeature f : collectionFeatures) {
assertThat(ArrayUtils.contains(f.getSupportedTypes(), collectionUniqueType), equalTo(true));
alwaysTrueFound = alwaysTrueFound || AlwaysTrueFeature.NAME.equals(f.getName());
}
// finally check that not existing type will return an empty list
final List<AuthorizationFeature> notExistingTypeFeatures = authzFeatureService
.findByResourceType("NOT-EXISTING-TYPE");
assertThat(notExistingTypeFeatures.size(), equalTo(0));
}
@Test
/**
* The isAuthorized must return true for authorized feature and false for not authorized feature
*
* @throws Exception
*/
public void isAuthorizedTest() throws Exception {
Site site = siteService.findSite(context);
SiteRest siteRest = converterService.toRest(site, DefaultProjection.DEFAULT);
AuthorizationFeature alwaysTrue = authzFeatureService.find(AlwaysTrueFeature.NAME);
AuthorizationFeature alwaysFalse = authzFeatureService.find(AlwaysFalseFeature.NAME);
AuthorizationFeature alwaysThrowEx = authzFeatureService.find(AlwaysThrowExceptionFeature.NAME);
AuthorizationFeature trueForAdmins = authzFeatureService.find(TrueForAdminsFeature.NAME);
assertThat(authzFeatureService.isAuthorized(context, alwaysTrue, siteRest), equalTo(true));
assertThat(authzFeatureService.isAuthorized(context, alwaysFalse, siteRest), equalTo(false));
try {
authzFeatureService.isAuthorized(context, alwaysThrowEx, siteRest);
// this code should be not run as the previous one throw an exception that we expect to be re-thrown
assertThat("the exception has been not re-thrown!", false, equalTo(true));
} catch (Exception ex) {
// if this code is executed the exception was re-thrown
assertThat("exceptions are rethrown", true, equalTo(true));
}
assertThat(authzFeatureService.isAuthorized(context, trueForAdmins, siteRest), equalTo(false));
// login our admin
context.setCurrentUser(admin);
assertThat(authzFeatureService.isAuthorized(context, trueForAdmins, siteRest), equalTo(true));
// finally check that a null object will always result in false to be returned
assertThat(authzFeatureService.isAuthorized(context, alwaysTrue, null), equalTo(false));
// without call at all the authorizationFeature to prevent NPE
assertThat(authzFeatureService.isAuthorized(context, alwaysThrowEx, null), equalTo(false));
}
}

View File

@@ -351,6 +351,61 @@ public class BitstreamControllerIT extends AbstractControllerIntegrationTest {
}
@Test
public void putOnBitstreamInOneBundleForbiddenTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Item publicItem1 = ItemBuilder.createItem(context, col1)
.withTitle("Test")
.withIssueDate("2016-11-11")
.withAuthor("Smith, Donald")
.withSubject("ExtraEntry")
.build();
Item targetItem = ItemBuilder.createItem(context, col1)
.withTitle("Test")
.withIssueDate("2016-11-11")
.withAuthor("Smith, Donald")
.withSubject("ExtraEntry")
.build();
Bundle bundle1 = BundleBuilder.createBundle(context, publicItem1)
.withName("TEST FIRST BUNDLE")
.build();
Bundle targetBundle = BundleBuilder.createBundle(context, targetItem)
.withName("TARGET BUNDLE")
.build();
String bitstreamContent = "ThisIsSomeDummyText";
Bitstream bitstream = null;
try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
bitstream = BitstreamBuilder.createBitstream(context, bundle1, is)
.withName("Bitstream")
.withDescription("description")
.withMimeType("text/plain")
.build();
}
context.restoreAuthSystemState();
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(put("/api/core/bitstreams/" + bitstream.getID() + "/bundle")
.contentType(parseMediaType(TEXT_URI_LIST_VALUE))
.content("https://localhost:8080/spring-rest/api/core/bundles/" + targetBundle.getID()))
.andExpect(status().isForbidden());
}
@Test
public void putOnBitstreamInMultipleBundles() throws Exception {

View File

@@ -8,7 +8,6 @@
package org.dspace.app.rest;
import static java.util.UUID.randomUUID;
import static org.apache.commons.codec.CharEncoding.UTF_8;
import static org.apache.commons.collections.CollectionUtils.isEmpty;
import static org.apache.commons.io.IOUtils.toInputStream;

View File

@@ -45,6 +45,7 @@ import org.dspace.app.rest.model.patch.MoveOperation;
import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.Collection;
@@ -54,10 +55,14 @@ import org.dspace.eperson.EPerson;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.MvcResult;
public class BundleRestRepositoryIT extends AbstractControllerIntegrationTest {
@Autowired
ResourcePolicyService resourcePolicyService;
private Collection collection;
private Item item;
private Bundle bundle1;
@@ -136,6 +141,30 @@ public class BundleRestRepositoryIT extends AbstractControllerIntegrationTest {
;
}
@Test
public void findOneForbiddenTest() throws Exception {
context.turnOffAuthorisationSystem();
String bitstreamContent = "Dummy content";
try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
bitstream1 = BitstreamBuilder.createBitstream(context, item, is)
.withName("Bitstream")
.withMimeType("text/plain")
.build();
}
bundle1 = BundleBuilder.createBundle(context, item)
.withName("testname")
.withBitstream(bitstream1)
.build();
resourcePolicyService.removePolicies(context, bundle1, Constants.READ);
context.restoreAuthSystemState();
String tokenEperson = getAuthToken(eperson.getEmail(), password);
getClient(tokenEperson).perform(get("/api/core/bundles/" + bundle1.getID()))
.andExpect(status().isForbidden());
}
@Test
public void getItemBundles() throws Exception {
@@ -385,6 +414,38 @@ public class BundleRestRepositoryIT extends AbstractControllerIntegrationTest {
)));
}
@Test
public void getBitstreamsForBundleForbiddenTest() throws Exception {
context.turnOffAuthorisationSystem();
String bitstreamContent = "Dummy content";
try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
bitstream1 = BitstreamBuilder.createBitstream(context, item, is)
.withName("Bitstream")
.withDescription("Description")
.withMimeType("text/plain")
.build();
bitstream2 = BitstreamBuilder.createBitstream(context, item, is)
.withName("Bitstream2")
.withDescription("Description2")
.withMimeType("text/plain")
.build();
}
bundle1 = BundleBuilder.createBundle(context, item)
.withName("testname")
.withBitstream(bitstream1)
.withBitstream(bitstream2)
.build();
resourcePolicyService.removePolicies(context, bundle1, Constants.READ);
context.restoreAuthSystemState();
String tokenEperson = getAuthToken(eperson.getEmail(), password);
getClient(tokenEperson).perform(get("/api/core/bundles/" + bundle1.getID() + "/bitstreams"))
.andExpect(status().isForbidden());
}
@Test
public void patchMoveBitstreams() throws Exception {
context.turnOffAuthorisationSystem();

View File

@@ -24,6 +24,7 @@ import java.util.UUID;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.builder.EPersonBuilder;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.matcher.CollectionMatcher;
import org.dspace.app.rest.matcher.CommunityMatcher;
@@ -37,6 +38,7 @@ import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.app.rest.test.MetadataPatchSuite;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.core.Constants;
@@ -54,6 +56,9 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
@Autowired
AuthorizeService authorizeService;
@Autowired
ResourcePolicyService resoucePolicyService;
@Test
public void findAllTest() throws Exception {
@@ -86,6 +91,108 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
)));
}
@Test
public void findAllUnAuthenticatedTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 1")
.build();
Collection col2 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 2")
.build();
resoucePolicyService.removePolicies(context, col2, Constants.READ);
context.restoreAuthSystemState();
// anonymous can see only public collections
getClient().perform(get("/api/core/collections"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.collections", Matchers.contains(
CollectionMatcher.matchCollection(col1))))
.andExpect(jsonPath("$.page.totalElements", is(1)));
}
@Test
public void findAllForbiddenTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 1")
.build();
Collection col2 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 2")
.build();
resoucePolicyService.removePolicies(context, col2, Constants.READ);
context.restoreAuthSystemState();
// eperson logged can see only public collections
String tokenEperson = getAuthToken(eperson.getEmail(), password);
getClient(tokenEperson).perform(get("/api/core/collections"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.collections", Matchers.contains(
CollectionMatcher.matchCollection(col1))))
.andExpect(jsonPath("$.page.totalElements", is(1)));
}
@Test
public void findAllGrantAccessAdminsTest() throws Exception {
context.turnOffAuthorisationSystem();
EPerson parentAdmin = EPersonBuilder.createEPerson(context)
.withEmail("eperson1@mail.com")
.withPassword("qwerty01")
.build();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.withAdminGroup(parentAdmin)
.build();
EPerson col1Admin = EPersonBuilder.createEPerson(context)
.withEmail("eperson2@mail.com")
.withPassword("qwerty02")
.build();
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 1")
.withAdminGroup(col1Admin)
.build();
Collection col2 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 2")
.build();
resoucePolicyService.removePolicies(context, parentCommunity, Constants.READ);
resoucePolicyService.removePolicies(context, col1, Constants.READ);
context.restoreAuthSystemState();
// parent community admin can see all sub collections
String tokenParentAdmin = getAuthToken(parentAdmin.getEmail(), "qwerty01");
getClient(tokenParentAdmin).perform(get("/api/core/collections"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.collections", Matchers.containsInAnyOrder(
CollectionMatcher.matchCollection(col1),
CollectionMatcher.matchCollection(col2))))
.andExpect(jsonPath("$.page.totalElements", is(2)));
// admin of col1 can see owner collections and any public collections
String tokenCol1Admin = getAuthToken(col1Admin.getEmail(), "qwerty02");
getClient(tokenCol1Admin).perform(get("/api/core/collections"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.collections", Matchers.containsInAnyOrder(
CollectionMatcher.matchCollection(col1),
CollectionMatcher.matchCollection(col2))))
.andExpect(jsonPath("$.page.totalElements", is(2)));
}
@Test
public void findAllPaginationTest() throws Exception {
@@ -182,6 +289,100 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
col1.getName(), col1.getID(), col1.getHandle())));
}
@Test
public void findOneCollectionUnAuthenticatedTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 1")
.build();
resoucePolicyService.removePolicies(context, col1, Constants.READ);
context.restoreAuthSystemState();
getClient().perform(get("/api/core/collections/" + col1.getID()))
.andExpect(status().isUnauthorized());
}
@Test
public void findOneCollectionForbiddenTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 1")
.build();
resoucePolicyService.removePolicies(context, col1, Constants.READ);
context.restoreAuthSystemState();
String tokenEperson = getAuthToken(eperson.getEmail(), password);
getClient(tokenEperson).perform(get("/api/core/collections/" + col1.getID()))
.andExpect(status().isForbidden());
}
@Test
public void findOneCollectionGrantAccessAdminsTest() throws Exception {
context.turnOffAuthorisationSystem();
EPerson parentAdmin = EPersonBuilder.createEPerson(context)
.withEmail("eperson1@mail.com")
.withPassword("qwerty01")
.build();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.withAdminGroup(parentAdmin)
.build();
EPerson col1Admin = EPersonBuilder.createEPerson(context)
.withEmail("eperson2@mail.com")
.withPassword("qwerty02")
.build();
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 1")
.withAdminGroup(col1Admin)
.build();
EPerson col2Admin = EPersonBuilder.createEPerson(context)
.withEmail("eperson3@mail.com")
.withPassword("qwerty03")
.build();
Collection col2 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 2")
.withAdminGroup(col2Admin)
.build();
resoucePolicyService.removePolicies(context, parentCommunity, Constants.READ);
resoucePolicyService.removePolicies(context, col1, Constants.READ);
resoucePolicyService.removePolicies(context, col2, Constants.READ);
context.restoreAuthSystemState();
String tokenParentAdmin = getAuthToken(parentAdmin.getEmail(), "qwerty01");
getClient(tokenParentAdmin).perform(get("/api/core/collections/" + col1.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$",
Matchers.is((CollectionMatcher.matchCollection(col1)))));
String tokenCol1Admin = getAuthToken(col1Admin.getEmail(), "qwerty02");
getClient(tokenCol1Admin).perform(get("/api/core/collections/" + col1.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$",
Matchers.is((CollectionMatcher.matchCollection(col1)))));
String tokenCol2Admin = getAuthToken(col2Admin.getEmail(), "qwerty03");
getClient(tokenCol2Admin).perform(get("/api/core/collections/" + col1.getID()))
.andExpect(status().isForbidden());
}
@Test
public void findOneCollectionRelsTest() throws Exception {

View File

@@ -32,7 +32,9 @@ import java.util.stream.StreamSupport;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.builder.EPersonBuilder;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.matcher.CollectionMatcher;
import org.dspace.app.rest.matcher.CommunityMatcher;
import org.dspace.app.rest.matcher.HalMatcher;
import org.dspace.app.rest.matcher.MetadataMatcher;
@@ -44,6 +46,7 @@ import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.app.rest.test.MetadataPatchSuite;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.service.CommunityService;
@@ -71,6 +74,9 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
@Autowired
AuthorizeService authorizeService;
@Autowired
ResourcePolicyService resoucePolicyService;
@Test
public void createTest() throws Exception {
ObjectMapper mapper = new ObjectMapper();
@@ -528,6 +534,110 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
;
}
@Test
public void findAllUnAuthenticatedTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community 2")
.build();
resoucePolicyService.removePolicies(context, parentCommunity, Constants.READ);
resoucePolicyService.removePolicies(context, child1, Constants.READ);
context.restoreAuthSystemState();
// anonymous can see only public communities
getClient().perform(get("/api/core/communities"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.communities", Matchers.contains(
CommunityMatcher.matchCommunity(child2))))
.andExpect(jsonPath("$.page.totalElements", is(1)));
}
@Test
public void findAllForbiddenTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community 2")
.build();
resoucePolicyService.removePolicies(context, parentCommunity, Constants.READ);
resoucePolicyService.removePolicies(context, child1, Constants.READ);
context.restoreAuthSystemState();
String tokenEperson = getAuthToken(eperson.getEmail(), password);
getClient(tokenEperson).perform(get("/api/core/communities"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.communities", Matchers.contains(
CommunityMatcher.matchCommunity(child2))))
.andExpect(jsonPath("$.page.totalElements", is(1)));
}
@Test
public void findAllGrantAccessAdminsTest() throws Exception {
context.turnOffAuthorisationSystem();
EPerson parentAdmin = EPersonBuilder.createEPerson(context)
.withEmail("eperson1@mail.com")
.withPassword("qwerty01")
.build();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.withAdminGroup(parentAdmin)
.build();
EPerson child1Admin = EPersonBuilder.createEPerson(context)
.withEmail("eperson2@mail.com")
.withPassword("qwerty02")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community 1")
.withAdminGroup(child1Admin)
.build();
Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community 2")
.build();
resoucePolicyService.removePolicies(context, parentCommunity, Constants.READ);
resoucePolicyService.removePolicies(context, child1, Constants.READ);
context.restoreAuthSystemState();
String tokenParentAdmin = getAuthToken(parentAdmin.getEmail(), "qwerty01");
getClient(tokenParentAdmin).perform(get("/api/core/communities"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.communities", Matchers.containsInAnyOrder(
CommunityMatcher.matchCommunity(parentCommunity),
CommunityMatcher.matchCommunity(child1),
CommunityMatcher.matchCommunity(child2))))
.andExpect(jsonPath("$.page.totalElements", is(3)));
String tokenChild1Admin = getAuthToken(child1Admin.getEmail(), "qwerty02");
getClient(tokenChild1Admin).perform(get("/api/core/communities"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.communities", Matchers.containsInAnyOrder(
CommunityMatcher.matchCommunity(child1),
CommunityMatcher.matchCommunity(child2))))
.andExpect(jsonPath("$.page.totalElements", is(2)));
}
@Test
public void findOneTest() throws Exception {
//We turn off the authorization system in order to create the structure as defined below
@@ -564,6 +674,86 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
parentCommunity.getName(), parentCommunity.getID(), parentCommunity.getHandle())));
}
@Test
public void findOneUnAuthenticatedTest() throws Exception {
context.turnOffAuthorisationSystem();
Community privateCommunity = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Private Community")
.build();
resoucePolicyService.removePolicies(context, privateCommunity, Constants.READ);
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities/" + privateCommunity.getID().toString()))
.andExpect(status().isUnauthorized());
}
@Test
public void findOneForbiddenTest() throws Exception {
context.turnOffAuthorisationSystem();
Community privateCommunity = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Private Community")
.build();
resoucePolicyService.removePolicies(context, privateCommunity, Constants.READ);
context.restoreAuthSystemState();
String tokenEperson = getAuthToken(eperson.getEmail(), password);
getClient(tokenEperson).perform(get("/api/core/communities/" + privateCommunity.getID().toString()))
.andExpect(status().isForbidden());
}
@Test
public void findOneGrantAccessAdminsTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.withAdminGroup(eperson)
.build();
EPerson privateCommunityAdmin = EPersonBuilder.createEPerson(context)
.withEmail("comunityAdmin@mail.com")
.withPassword("qwerty01")
.build();
Community privateCommunity = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.withAdminGroup(privateCommunityAdmin)
.build();
EPerson privateCommunityAdmin2 = EPersonBuilder.createEPerson(context)
.withEmail("comunityAdmin2@mail.com")
.withPassword("qwerty02")
.build();
Community privateCommunity2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community 2")
.withAdminGroup(privateCommunityAdmin2)
.build();
resoucePolicyService.removePolicies(context, privateCommunity, Constants.READ);
context.restoreAuthSystemState();
String tokenParentComunityAdmin = getAuthToken(eperson.getEmail(), password);
getClient(tokenParentComunityAdmin).perform(get("/api/core/communities/" + privateCommunity.getID().toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(CommunityMatcher.matchCommunity(privateCommunity))));
String tokenCommunityAdmin = getAuthToken(privateCommunityAdmin.getEmail(), "qwerty01");
getClient(tokenCommunityAdmin).perform(get("/api/core/communities/" + privateCommunity.getID().toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(CommunityMatcher.matchCommunity(privateCommunity))));
String tokenComunityAdmin2 = getAuthToken(privateCommunityAdmin2.getEmail(), "qwerty02");
getClient(tokenComunityAdmin2).perform(get("/api/core/communities/"
+ privateCommunity.getID().toString()))
.andExpect(status().isForbidden());
}
@Test
public void findOneRelsTest() throws Exception {
//We turn off the authorization system in order to create the structure as defined below
@@ -615,7 +805,7 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
getClient().perform(get("/api/core/communities/" + child1.getID().toString() + "/logo"))
.andExpect(status().isNoContent());
//Main community has no collections, therefore contentType is not set
//Main community has no collections
getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString() + "/collections"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.totalElements", is(0)));
@@ -698,31 +888,30 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
//** GIVEN **
//1. A community-collection structure with one parent community with sub-community and one collection.
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.withLogo("ThisIsSomeDummyText")
.build();
.withName("Parent Community")
.withLogo("ThisIsSomeDummyText")
.build();
Community parentCommunity2 = CommunityBuilder.createCommunity(context)
.withName("Parent Community 2")
.withLogo("SomeTest")
.build();
.withName("Parent Community 2")
.withLogo("SomeTest")
.build();
Community parentCommunityChild1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
.withName("Sub Community")
.build();
Community parentCommunityChild2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community2")
.build();
.withName("Sub Community2")
.build();
Community parentCommunityChild2Child1 = CommunityBuilder.createSubCommunity(context, parentCommunityChild2)
.withName("Sub Sub Community")
.build();
.withName("Sub Sub Community")
.build();
Community parentCommunity2Child1 = CommunityBuilder.createSubCommunity(context, parentCommunity2)
.withName("Sub2 Community")
.build();
.withName("Sub2 Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, parentCommunityChild1)
.withName("Collection 1")
@@ -730,98 +919,379 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities/search/subCommunities")
.param("parent", parentCommunity.getID().toString())
.param("projection", "full"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
//Checking that these communities are present
.andExpect(jsonPath("$._embedded.communities", Matchers.containsInAnyOrder(
CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild1.getName(),
parentCommunityChild1.getID(),
parentCommunityChild1.getHandle()),
CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild2.getName(),
parentCommunityChild2.getID(),
parentCommunityChild2.getHandle())
)))
//Checking that these communities are not present
.andExpect(jsonPath("$._embedded.communities", Matchers.not(Matchers.anyOf(
CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity.getName(),
parentCommunity.getID(),
parentCommunity.getHandle()),
CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity2.getName(),
parentCommunity2.getID(),
parentCommunity2.getHandle()),
CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity2Child1.getName(),
parentCommunity2Child1.getID(),
parentCommunity2Child1.getHandle()),
CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild2Child1.getName(),
parentCommunityChild2Child1.getID(),
parentCommunityChild2Child1.getHandle())
))))
.andExpect(jsonPath("$._links.self.href",
Matchers.containsString("/api/core/communities/search/subCommunities")))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(2)))
;
getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString() + "/subcommunities"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
//Checking that these communities are present
.andExpect(jsonPath("$._embedded.subcommunities", Matchers.containsInAnyOrder(
CommunityMatcher.matchCommunity(parentCommunityChild1),
CommunityMatcher.matchCommunity(parentCommunityChild2)
)))
//Checking that these communities are not present
.andExpect(jsonPath("$._embedded.subcommunities", Matchers.not(Matchers.anyOf(
CommunityMatcher.matchCommunity(parentCommunity),
CommunityMatcher.matchCommunity(parentCommunity2),
CommunityMatcher.matchCommunity(parentCommunity2Child1),
CommunityMatcher.matchCommunity(parentCommunityChild2Child1)
))))
.andExpect(jsonPath("$._links.self.href",
Matchers.containsString("/api/core/communities/" + parentCommunity.getID().toString())))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(2)));
getClient().perform(get("/api/core/communities/search/subCommunities")
.param("parent", parentCommunityChild2.getID().toString())
.param("projection", "full"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
//Checking that these communities are present
.andExpect(jsonPath("$._embedded.communities", Matchers.contains(
CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild2Child1.getName(),
parentCommunityChild2Child1.getID(),
parentCommunityChild2Child1.getHandle())
)))
//Checking that these communities are not present
.andExpect(jsonPath("$._embedded.communities", Matchers.not(Matchers.anyOf(
CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity.getName(),
parentCommunity.getID(),
parentCommunity.getHandle()),
CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity2.getName(),
parentCommunity2.getID(),
parentCommunity2.getHandle()),
CommunityMatcher.matchCommunityEntryFullProjection(parentCommunity2Child1.getName(),
parentCommunity2Child1.getID(),
parentCommunity2Child1.getHandle()),
CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild2Child1.getName(),
parentCommunityChild2Child1.getID(),
parentCommunityChild2Child1.getHandle()),
CommunityMatcher.matchCommunityEntryFullProjection(parentCommunityChild1.getName(),
parentCommunityChild1.getID(),
parentCommunityChild1.getHandle())
))))
.andExpect(jsonPath("$._links.self.href",
Matchers.containsString("/api/core/communities/search/subCommunities")))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(1)))
;
getClient().perform(get("/api/core/communities/" + parentCommunityChild2.getID().toString() + "/subcommunities"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
//Checking that these communities are present
.andExpect(jsonPath("$._embedded.subcommunities", Matchers.contains(
CommunityMatcher.matchCommunity(parentCommunityChild2Child1)
)))
//Checking that these communities are not present
.andExpect(jsonPath("$._embedded.subcommunities", Matchers.not(Matchers.anyOf(
CommunityMatcher.matchCommunity(parentCommunity),
CommunityMatcher.matchCommunity(parentCommunity2),
CommunityMatcher.matchCommunity(parentCommunity2Child1),
CommunityMatcher.matchCommunity(parentCommunityChild2Child1),
CommunityMatcher.matchCommunity(parentCommunityChild1)
))))
.andExpect(jsonPath("$._links.self.href",
Matchers.containsString("/api/core/communities/" + parentCommunityChild2.getID().toString())))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(1)));
getClient().perform(get("/api/core/communities/search/subCommunities")
.param("parent", parentCommunityChild2Child1.getID().toString()))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._links.self.href",
Matchers.containsString("/api/core/communities/search/subCommunities")))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(0)))
;
getClient().perform(get("/api/core/communities/"
+ parentCommunityChild2Child1.getID().toString() + "/subcommunities"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._links.self.href",
Matchers.containsString("/api/core/communities/" + parentCommunityChild2Child1.getID().toString())))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(0)));
}
@Test
public void findAllSubCommunitiesWithoutUUID() throws Exception {
getClient().perform(get("/api/core/communities/search/subCommunities"))
.andExpect(status().isBadRequest());
public void findAllSubCommunitiesUnAuthenticatedTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.withLogo("ThisIsSomeDummyText")
.build();
Community communityChild1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Community communityChild2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community2")
.build();
Community communityChild2Child1 = CommunityBuilder.createSubCommunity(context, communityChild2)
.withName("Sub Community2")
.build();
resoucePolicyService.removePolicies(context, communityChild2, Constants.READ);
context.restoreAuthSystemState();
// anonymous can NOT see the private communities
getClient().perform(get("/api/core/communities/" + communityChild2.getID().toString() + "/subcommunities"))
.andExpect(status().isUnauthorized());
// anonymous can see only public communities
getClient().perform(get("/api/core/communities/" + parentCommunity.getID().toString() + "/subcommunities"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.subcommunities", Matchers.contains(
CommunityMatcher.matchCommunity(communityChild1))))
.andExpect(jsonPath("$.page.totalElements", is(1)));
// admin can see all communities
String tokenAdmin = getAuthToken(admin.getEmail(), password);
getClient(tokenAdmin).perform(get("/api/core/communities/"
+ parentCommunity.getID().toString() + "/subcommunities"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.subcommunities", Matchers.containsInAnyOrder(
CommunityMatcher.matchCommunity(communityChild1),
CommunityMatcher.matchCommunity(communityChild2))))
.andExpect(jsonPath("$.page.totalElements", is(2)));
}
@Test
public void findAllSubCommunitiesForbiddenTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.withLogo("ThisIsSomeDummyText")
.build();
Community communityChild1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Community communityChild2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community2")
.build();
Community communityChild2Child1 = CommunityBuilder.createSubCommunity(context, communityChild2)
.withName("Sub Community2")
.build();
resoucePolicyService.removePolicies(context, communityChild2, Constants.READ);
context.restoreAuthSystemState();
String tokenEperson = getAuthToken(eperson.getEmail(), password);
getClient(tokenEperson).perform(get("/api/core/communities/"
+ parentCommunity.getID().toString() + "/subcommunities"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.subcommunities", Matchers.contains(
CommunityMatcher.matchCommunity(communityChild1))))
.andExpect(jsonPath("$.page.totalElements", is(1)));
getClient(tokenEperson).perform(get("/api/core/communities/"
+ communityChild2.getID().toString() + "/subcommunities"))
.andExpect(status().isForbidden());
}
@Test
public void findAllSubCommunitiesGrantAccessAdminsTest() throws Exception {
context.turnOffAuthorisationSystem();
EPerson parentComAdmin = EPersonBuilder.createEPerson(context)
.withEmail("eperson1@mail.com")
.withPassword("qwerty01")
.build();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.withLogo("ThisIsSomeDummyText")
.withAdminGroup(parentComAdmin)
.build();
EPerson child1Admin = EPersonBuilder.createEPerson(context)
.withEmail("eperson2@mail.com")
.withPassword("qwerty02")
.build();
Community communityChild1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.withAdminGroup(child1Admin)
.build();
EPerson child2Admin = EPersonBuilder.createEPerson(context)
.withEmail("eperson3@mail.com")
.withPassword("qwerty03")
.build();
Community communityChild2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community2")
.withAdminGroup(child2Admin)
.build();
Community ommunityChild1Child1 = CommunityBuilder.createSubCommunity(context, communityChild1)
.withName("Sub1 Community 1")
.build();
Community сommunityChild2Child1 = CommunityBuilder.createSubCommunity(context, communityChild2)
.withName("Sub2 Community 1")
.build();
resoucePolicyService.removePolicies(context, parentCommunity, Constants.READ);
resoucePolicyService.removePolicies(context, communityChild1, Constants.READ);
resoucePolicyService.removePolicies(context, communityChild2, Constants.READ);
context.restoreAuthSystemState();
String tokenParentAdmin = getAuthToken(parentComAdmin.getEmail(), "qwerty01");
getClient(tokenParentAdmin).perform(get("/api/core/communities/"
+ parentCommunity.getID().toString() + "/subcommunities"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.subcommunities", Matchers.containsInAnyOrder(
CommunityMatcher.matchCommunity(communityChild1),
CommunityMatcher.matchCommunity(communityChild2))))
.andExpect(jsonPath("$.page.totalElements", is(2)));
String tokenChild1Admin = getAuthToken(child1Admin.getEmail(), "qwerty02");
getClient(tokenChild1Admin).perform(get("/api/core/communities/"
+ parentCommunity.getID().toString() + "/subcommunities"))
.andExpect(status().isForbidden());
String tokenChild2Admin = getAuthToken(child2Admin.getEmail(), "qwerty03");
getClient(tokenChild2Admin).perform(get("/api/core/communities/"
+ communityChild1.getID().toString() + "/subcommunities"))
.andExpect(status().isForbidden());
}
@Test
public void findAllCollectionsUnAuthenticatedTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.withLogo("ThisIsSomeDummyText")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Collection child1Col1 = CollectionBuilder.createCollection(context, child1)
.withName("Collection 1 child 1")
.build();
Collection child1Col2 = CollectionBuilder.createCollection(context, child1)
.withName("Collection 2 child 1")
.build();
Collection child2Col1 = CollectionBuilder.createCollection(context, child2)
.withName("Collection 1 child 2")
.build();
resoucePolicyService.removePolicies(context, child1Col2, Constants.READ);
resoucePolicyService.removePolicies(context, child2, Constants.READ);
context.restoreAuthSystemState();
// anonymous can see only public communities
getClient().perform(get("/api/core/communities/" + child1.getID().toString() + "/collections"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.collections", Matchers.contains(CollectionMatcher
.matchCollection(child1Col1))))
.andExpect(jsonPath("$.page.totalElements", is(1)));
// anonymous can NOT see the private communities
getClient().perform(get("/api/core/communities/" + child2.getID().toString() + "/collections"))
.andExpect(status().isUnauthorized());
}
@Test
public void findAllCollectionsForbiddenTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.withLogo("ThisIsSomeDummyText")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community 1")
.build();
Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community 2")
.build();
Collection child1Col1 = CollectionBuilder.createCollection(context, child1)
.withName("Collection 1 child 1")
.build();
Collection child1Col2 = CollectionBuilder.createCollection(context, child1)
.withName("Collection 2 child 1")
.build();
Collection child2Col1 = CollectionBuilder.createCollection(context, child2)
.withName("Collection 1 child 2")
.build();
resoucePolicyService.removePolicies(context, child1Col2, Constants.READ);
resoucePolicyService.removePolicies(context, child2, Constants.READ);
context.restoreAuthSystemState();
String tokenAdmin = getAuthToken(admin.getEmail(), password);
getClient(tokenAdmin).perform(get("/api/core/communities/" + child1.getID().toString() + "/collections"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.collections", Matchers.containsInAnyOrder(
CollectionMatcher.matchCollection(child1Col1),
CollectionMatcher.matchCollection(child1Col2))))
.andExpect(jsonPath("$.page.totalElements", is(2)));
getClient(tokenAdmin).perform(get("/api/core/communities/" + child2.getID().toString() + "/collections"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.collections", Matchers.contains(
CollectionMatcher.matchCollection(child2Col1))))
.andExpect(jsonPath("$.page.totalElements", is(1)));
String tokenEperson = getAuthToken(eperson.getEmail(), password);
getClient(tokenEperson).perform(get("/api/core/communities/" + child2.getID().toString() + "/collections"))
.andExpect(status().isForbidden());
}
@Test
public void findAllCollectionsGrantAccessAdminsTest() throws Exception {
context.turnOffAuthorisationSystem();
EPerson parentAdmin = EPersonBuilder.createEPerson(context)
.withEmail("eperson1@mail.com")
.withPassword("qwerty01")
.build();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.withLogo("ThisIsSomeDummyText")
.withAdminGroup(parentAdmin)
.build();
EPerson child1Admin = EPersonBuilder.createEPerson(context)
.withEmail("child1admin@mail.com")
.withPassword("qwerty02")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.withAdminGroup(child1Admin)
.build();
EPerson child2Admin = EPersonBuilder.createEPerson(context)
.withEmail("child2admin@mail.com")
.withPassword("qwerty03")
.build();
Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.withAdminGroup(child2Admin)
.build();
Collection child1Col1 = CollectionBuilder.createCollection(context, child1)
.withName("Child 1 Collection 1")
.build();
Collection child1Col2 = CollectionBuilder.createCollection(context, child1)
.withName("Child 1 Collection 2")
.build();
Collection child2Col1 = CollectionBuilder.createCollection(context, child2)
.withName("Child 2 Collection 1")
.build();
resoucePolicyService.removePolicies(context, child1Col2, Constants.READ);
resoucePolicyService.removePolicies(context, child2, Constants.READ);
context.restoreAuthSystemState();
String tokenParentAdmin = getAuthToken(parentAdmin.getEmail(), "qwerty01");
getClient(tokenParentAdmin).perform(get("/api/core/communities/" + child1.getID().toString() + "/collections"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.collections", Matchers.containsInAnyOrder(
CollectionMatcher.matchCollection(child1Col1),
CollectionMatcher.matchCollection(child1Col2))))
.andExpect(jsonPath("$.page.totalElements", is(2)));
getClient(tokenParentAdmin).perform(get("/api/core/communities/" + child2.getID().toString() + "/collections"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.collections", Matchers.contains(
CollectionMatcher.matchCollection(child2Col1))))
.andExpect(jsonPath("$.page.totalElements", is(1)));
String tokenChild2Admin = getAuthToken(child2Admin.getEmail(), "qwerty03");
getClient(tokenChild2Admin).perform(get("/api/core/communities/" + child1.getID().toString() + "/collections"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.collections", Matchers.contains(
CollectionMatcher.matchCollection(child1Col1))))
.andExpect(jsonPath("$.page.totalElements", is(1)));
}
@Test
public void findAllSubCommunitiesWithUnexistentUUID() throws Exception {
getClient().perform(get("/api/core/communities/search/subCommunities")
.param("parent", UUID.randomUUID().toString()))
.andExpect(status().isNotFound());
getClient().perform(get("/api/core/communities/" + UUID.randomUUID().toString() + "/subcommunities"))
.andExpect(status().isNotFound());
}
@Test

View File

@@ -235,6 +235,28 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest {
}
@Test
public void findOneForbiddenTest() throws Exception {
context.turnOffAuthorisationSystem();
EPerson ePerson1 = EPersonBuilder.createEPerson(context)
.withNameInMetadata("Mik", "Reck")
.withEmail("MikReck@email.com")
.withPassword("qwerty01")
.build();
EPerson ePerson2 = EPersonBuilder.createEPerson(context)
.withNameInMetadata("Bob", "Smith")
.withEmail("bobsmith@fake-email.com")
.build();
context.restoreAuthSystemState();
String tokenEperson1 = getAuthToken(ePerson1.getEmail(), "qwerty01");
getClient(tokenEperson1).perform(get("/api/eperson/epersons/" + ePerson2.getID()))
.andExpect(status().isForbidden());
}
@Test
public void readEpersonAuthorizationTest() throws Exception {
context.turnOffAuthorisationSystem();
@@ -1494,6 +1516,31 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest {
new MetadataPatchSuite().runWith(getClient(token), "/api/eperson/epersons/" + ePerson.getID(), expectedStatus);
}
@Test
public void newlyCreatedAccountHasNoGroups() throws Exception {
context.turnOffAuthorisationSystem();
EPerson ePerson1 = EPersonBuilder.createEPerson(context)
.withNameInMetadata("Mik", "Reck")
.withEmail("MikReck@email.com")
.withPassword("qwerty01")
.build();
context.restoreAuthSystemState();
String tokenEperson1 = getAuthToken(ePerson1.getEmail(), "qwerty01");
// by contract the groups embedded in the eperson only contains direct explicit membership,
// so the anonymous group is not listed
getClient(tokenEperson1).perform(get("/api/eperson/epersons/" + ePerson1.getID())
.param("projection", "full"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$", Matchers.allOf(
hasJsonPath("$._embedded.groups._embedded.groups.length()", is(0)),
hasJsonPath("$._embedded.groups.page.totalElements", is(0))
)));
}
/**
* Test that epersons/<:uuid>/groups endpoint returns the direct groups of the epersons
* @throws Exception
@@ -1543,5 +1590,4 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest {
);
}
}

View File

@@ -43,11 +43,13 @@ import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.model.patch.ReplaceOperation;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.app.rest.test.MetadataPatchSuite;
import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.CommunityService;
import org.dspace.core.Constants;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.eperson.factory.EPersonServiceFactory;
@@ -55,6 +57,7 @@ import org.dspace.eperson.service.EPersonService;
import org.dspace.eperson.service.GroupService;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author Jonas Van Goolen - (jonas@atmire.com)
@@ -62,6 +65,9 @@ import org.junit.Test;
public class GroupRestRepositoryIT extends AbstractControllerIntegrationTest {
@Autowired
ResourcePolicyService resourcePolicyService;
@Test
public void createTest()
throws Exception {
@@ -195,6 +201,13 @@ public class GroupRestRepositoryIT extends AbstractControllerIntegrationTest {
;
}
@Test
public void findAllForbiddenTest() throws Exception {
String tokenEperson = getAuthToken(eperson.getEmail(), password);
getClient(tokenEperson).perform(get("/api/eperson/groups"))
.andExpect(status().isForbidden());
}
@Test
public void findAllPaginationTest() throws Exception {
@@ -304,6 +317,22 @@ public class GroupRestRepositoryIT extends AbstractControllerIntegrationTest {
Matchers.containsString("/api/eperson/groups/" + group2.getID())));
}
@Test
public void findOneForbiddenTest() throws Exception {
context.turnOffAuthorisationSystem();
Group privateGroup = GroupBuilder.createGroup(context)
.withName("Private Group")
.build();
resourcePolicyService.removePolicies(context, privateGroup, Constants.READ);
context.restoreAuthSystemState();
String tokenEperson = getAuthToken(eperson.getEmail(), password);
getClient(tokenEperson).perform(get("/api/eperson/groups/" + privateGroup.getID()))
.andExpect(status().isForbidden());
}
@Test
public void findOneTestWrongUUID() throws Exception {
context.turnOffAuthorisationSystem();

View File

@@ -283,4 +283,34 @@ public class ItemOwningCollectionUpdateRestControllerIT extends AbstractControll
}
@Test
public void moveItemForbiddenTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 1")
.build();
Collection col2 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 2")
.build();
Item publicItem1 = ItemBuilder.createItem(context, col1)
.withTitle("Public item 1")
.withIssueDate("2019-10-21")
.withAuthor("Smith, Donald")
.build();
context.restoreAuthSystemState();
String tokenEPerson = getAuthToken(eperson.getEmail(), password);
getClient(tokenEPerson).perform(put("/api/core/items/" + publicItem1.getID() + "/owningCollection/")
.contentType(parseMediaType(TEXT_URI_LIST_VALUE))
.content("https://localhost:8080/spring-rest/api/core/collections/" + col2.getID()))
.andExpect(status().isForbidden());
}
}

View File

@@ -8,7 +8,6 @@
package org.dspace.app.rest;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@@ -124,6 +123,13 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
;
}
@Test
public void findAllForbiddenTest() throws Exception {
String tokenEperson = getAuthToken(eperson.getEmail(), password);
getClient(tokenEperson).perform(get("/api/core/items"))
.andExpect(status().isForbidden());
}
@Test
public void findAllWithPaginationTest() throws Exception {
context.turnOffAuthorisationSystem();

View File

@@ -32,14 +32,20 @@ import org.dspace.app.rest.model.patch.AddOperation;
import org.dspace.app.rest.model.patch.Operation;
import org.dspace.app.rest.model.patch.ReplaceOperation;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.Collection;
import org.dspace.core.Constants;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.MvcResult;
public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationTest {
@Autowired
ResourcePolicyService resourcePolicyService;
private ObjectMapper mapper;
private String adminAuthToken;
private Collection childCollection;
@@ -160,6 +166,17 @@ public class ItemTemplateRestControllerIT extends AbstractControllerIntegrationT
)));
}
@Test
public void getTemplateItemFromCollectionForbiddenTest() throws Exception {
setupTestTemplate();
String itemUuidString = installTestTemplate();
resourcePolicyService.removePolicies(context, childCollection, Constants.READ);
String tokenEperson = getAuthToken(eperson.getEmail(), password);
getClient(tokenEperson).perform(get(getCollectionTemplateItemUrlTemplate(childCollection.getID().toString())))
.andExpect(status().isForbidden());
}
@Test
public void getTemplateItemFromItemId() throws Exception {
setupTestTemplate();

View File

@@ -8,7 +8,6 @@
package org.dspace.app.rest;
import static com.jayway.jsonpath.JsonPath.read;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
@@ -27,11 +26,9 @@ import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import javax.ws.rs.core.MediaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.builder.EPersonBuilder;

View File

@@ -0,0 +1,42 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.junit.Test;
/**
* Integration test that cover ShibbolethRestController
*
* @author Giuseppe Digilio (giuseppe dot digilio at 4science dot it)
*/
public class ShibbolethRestControllerIT extends AbstractControllerIntegrationTest {
@Test
public void testRedirectToDefaultDspaceUrl() throws Exception {
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(get("/api/authn/shibboleth"))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("http://localhost:3000"));
}
@Test
public void testRedirectToGivenUrl() throws Exception {
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(get("/api/authn/shibboleth")
.param("redirectUrl", "http://dspace.org"))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("http://dspace.org"));
}
}

View File

@@ -8,6 +8,7 @@
package org.dspace.app.rest;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static org.dspace.app.rest.test.AbstractControllerIntegrationTest.REST_SERVER_URL;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.hasSize;
@@ -62,6 +63,22 @@ public class SubmissionDefinitionsControllerIT extends AbstractControllerIntegra
;
}
@Test
public void findAllWithNewlyCreatedAccountTest() throws Exception {
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(get("/api/config/submissiondefinitions"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", greaterThanOrEqualTo(1)))
.andExpect(jsonPath("$.page.totalPages", greaterThanOrEqualTo(1)))
.andExpect(jsonPath("$.page.number", is(0)))
.andExpect(jsonPath("$._links.search.href", is(REST_SERVER_URL
+ "config/submissiondefinitions/search")))
//The array of browse index should have a size greater or equals to 1
.andExpect(jsonPath("$._embedded.submissiondefinitions", hasSize(greaterThanOrEqualTo(1))));
}
@Test
public void findDefault() throws Exception {
getClient().perform(get("/api/config/submissiondefinitions/traditional"))
@@ -84,6 +101,19 @@ public class SubmissionDefinitionsControllerIT extends AbstractControllerIntegra
;
}
@Test
public void findOneWithNewlyCreatedAccountTest() throws Exception {
String tokenEPerson = getAuthToken(eperson.getEmail(), password);
getClient(tokenEPerson).perform(get("/api/config/submissiondefinitions/traditional"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$", allOf(
hasJsonPath("$.isDefault", is(true)),
hasJsonPath("$.name", is("traditional")),
hasJsonPath("$.id", is("traditional")),
hasJsonPath("$.type", is("submissiondefinition")))));
}
@Test
public void findByCollection() throws Exception {
@@ -117,6 +147,29 @@ public class SubmissionDefinitionsControllerIT extends AbstractControllerIntegra
.matchSubmissionDefinition(true, "traditional", "traditional")));
}
@Test
public void findByCollectionWithNewlyCreatedAccountTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 1")
.build();
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(get("/api/config/submissiondefinitions/search/findByCollection")
.param("uuid", col1.getID().toString()))
.andExpect(status().isOk())
.andDo(MockMvcResultHandlers.print())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$", SubmissionDefinitionsMatcher
.matchSubmissionDefinition(true, "traditional", "traditional")));
}
@Test
public void findCollections() throws Exception {

View File

@@ -55,6 +55,21 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
;
}
@Test
public void findAllWithNewlyCreatedAccountTest() throws Exception {
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(get("/api/config/submissionforms"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", equalTo(4)))
.andExpect(jsonPath("$.page.totalPages", equalTo(1)))
.andExpect(jsonPath("$.page.number", is(0)))
.andExpect(jsonPath("$._links.self.href", Matchers.startsWith(REST_SERVER_URL
+ "config/submissionforms")))
.andExpect(jsonPath("$._embedded.submissionforms", hasSize(equalTo(4))));
}
@Test
public void findTraditionalPageOne() throws Exception {
//When we call the root endpoint as anonymous user
@@ -96,6 +111,34 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
;
}
@Test
public void findTraditionalPageOneWithNewlyCreatedAccountTest() throws Exception {
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(get("/api/config/submissionforms/traditionalpageone"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.id", is("traditionalpageone")))
.andExpect(jsonPath("$.name", is("traditionalpageone")))
.andExpect(jsonPath("$.type", is("submissionform")))
.andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/traditionalpageone")))
.andExpect(jsonPath("$.rows[0].fields", contains(
SubmissionFormFieldMatcher.matchFormFieldDefinition("name", "Author",
null, true,"Add an author", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains(
SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Title",
"You must enter a main title for this item.", false,
"Enter the main title of the item.", "dc.title"))))
.andExpect(jsonPath("$.rows[3].fields",contains(
SubmissionFormFieldMatcher.matchFormFieldDefinition("date", "Date of Issue",
"You must enter at least the year.", false,
"Please give the date", "col-sm-4",
"dc.date.issued"),
SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Publisher",
null, false,"Enter the name of",
"col-sm-8","dc.publisher"))));
}
@Test
public void findOpenRelationshipConfig() throws Exception {
String token = getAuthToken(admin.getEmail(), password);

View File

@@ -33,7 +33,7 @@ public class SubmissionSectionsControllerIT extends AbstractControllerIntegratio
.andExpect(status().isUnauthorized());
String token = getAuthToken(admin.getEmail(), password);
String token = getAuthToken(eperson.getEmail(), password);
//When we call the root endpoint
getClient(token).perform(get("/api/config/submissionsections"))

View File

@@ -54,4 +54,19 @@ public class SubmissionUploadsControllerIT extends AbstractControllerIntegration
.andExpect(jsonPath("$._embedded.submissionuploads", hasSize(greaterThanOrEqualTo(1))))
;
}
@Test
public void findAllWithNewlyCreatedAccountTest() throws Exception {
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(get("/api/config/submissionuploads"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", greaterThanOrEqualTo(1)))
.andExpect(jsonPath("$.page.totalPages", greaterThanOrEqualTo(1)))
.andExpect(jsonPath("$.page.number", is(0)))
.andExpect(jsonPath("$._links.self.href",
Matchers.startsWith(REST_SERVER_URL + "config/submissionuploads")))
.andExpect(jsonPath("$._embedded.submissionuploads", hasSize(greaterThanOrEqualTo(1))));
}
}

View File

@@ -0,0 +1,162 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.sql.SQLException;
import java.util.Date;
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.builder.ItemBuilder;
import org.dspace.app.rest.matcher.VersionHistoryMatcher;
import org.dspace.app.rest.matcher.VersionMatcher;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.services.ConfigurationService;
import org.dspace.versioning.Version;
import org.dspace.versioning.VersionHistory;
import org.dspace.versioning.service.VersionHistoryService;
import org.dspace.versioning.service.VersioningService;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
public class VersionHistoryRestRepositoryIT extends AbstractControllerIntegrationTest {
VersionHistory versionHistory;
Item item;
Version version;
@Autowired
private ConfigurationService configurationService;
@Autowired
private VersionHistoryService versionHistoryService;
@Autowired
private VersioningService versioningService;
@Before
public void setup() throws SQLException, AuthorizeException {
context.turnOffAuthorisationSystem();
versionHistory = versionHistoryService.create(context);
//** GIVEN **
//1. A community-collection structure with one parent community with sub-community and two collections.
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
//2. Three public items that are readable by Anonymous with different subjects
item = ItemBuilder.createItem(context, col1)
.withTitle("Public item 1")
.withIssueDate("2017-10-17")
.withAuthor("Smith, Donald").withAuthor("Doe, John")
.withSubject("ExtraEntry")
.build();
version = versioningService.createNewVersion(context, versionHistory, item, "test", new Date(), 0);
context.restoreAuthSystemState();
}
@Test
public void findOneTest() throws Exception {
getClient().perform(get("/api/versioning/versionhistories/" + versionHistory.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", is(VersionHistoryMatcher.matchEntry(versionHistory))));
}
@Test
public void findOneForbiddenTest() throws Exception {
configurationService.setProperty("versioning.item.history.view.admin", true);
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(get("/api/versioning/versionhistories/" + versionHistory.getID()))
.andExpect(status().isForbidden());
configurationService.setProperty("versioning.item.history.view.admin", false);
}
@Test
public void findOneWrongIDTest() throws Exception {
int wrongVersionHistoryId = (versionHistory.getID() + 5) * 57;
getClient().perform(get("/api/versioning/versionhistories/" + wrongVersionHistoryId))
.andExpect(status().isNotFound());
}
@Test
public void findVersionsOneWrongIDTest() throws Exception {
int wrongVersionHistoryId = (versionHistory.getID() + 5) * 57;
getClient().perform(get("/api/versioning/versionhistories/" + wrongVersionHistoryId + "/versions"))
.andExpect(status().isNotFound());
}
@Test
public void findVersionsOfVersionHistoryTest() throws Exception {
Version version = versionHistoryService.getFirstVersion(context, versionHistory);
getClient().perform(get("/api/versioning/versionhistories/" + versionHistory.getID() + "/versions"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.versions", contains(VersionMatcher.matchEntry(version))));
context.turnOffAuthorisationSystem();
Version secondVersion = versioningService
.createNewVersion(context, versionHistory, item, "test", new Date(), 0);
context.restoreAuthSystemState();
getClient().perform(get("/api/versioning/versionhistories/" + versionHistory.getID() + "/versions"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.versions", containsInAnyOrder(VersionMatcher.matchEntry(version),
VersionMatcher
.matchEntry(secondVersion))));
}
@Test
public void findVersionsOfVersionHistoryPaginationTest() throws Exception {
context.turnOffAuthorisationSystem();
Version version = versionHistoryService.getFirstVersion(context, versionHistory);
Version secondVersion = versioningService
.createNewVersion(context, versionHistory, item, "test", new Date(), 0);
context.restoreAuthSystemState();
getClient().perform(get("/api/versioning/versionhistories/" + versionHistory.getID() + "/versions")
.param("size", "1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.versions", contains(VersionMatcher.matchEntry(secondVersion))))
.andExpect(jsonPath("$._embedded.versions",
Matchers.not(contains(VersionMatcher.matchEntry(version)))));
getClient().perform(get("/api/versioning/versionhistories/" + versionHistory.getID() + "/versions")
.param("size", "1")
.param("page", "1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.versions", contains(VersionMatcher.matchEntry(version))))
.andExpect(jsonPath("$._embedded.versions",
Matchers.not(contains(VersionMatcher.matchEntry(secondVersion)))));
}
}

View File

@@ -0,0 +1,192 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.builder.ItemBuilder;
import org.dspace.app.rest.matcher.ItemMatcher;
import org.dspace.app.rest.matcher.VersionMatcher;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.content.service.ItemService;
import org.dspace.services.ConfigurationService;
import org.dspace.versioning.Version;
import org.dspace.versioning.service.VersioningService;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
public class VersionRestRepositoryIT extends AbstractControllerIntegrationTest {
Item item;
Version version;
@Autowired
private ItemService itemService;
@Autowired
private VersioningService versioningService;
@Autowired
private ConfigurationService configurationService;
@Before
public void setup() {
context.turnOffAuthorisationSystem();
//** GIVEN **
//1. A community-collection structure with one parent community with sub-community and two collections.
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
//2. Three public items that are readable by Anonymous with different subjects
item = ItemBuilder.createItem(context, col1)
.withTitle("Public item 1")
.withIssueDate("2017-10-17")
.withAuthor("Smith, Donald").withAuthor("Doe, John")
.withSubject("ExtraEntry")
.build();
version = versioningService.createNewVersion(context, item);
context.restoreAuthSystemState();
}
@Test
public void findOneTest() throws Exception {
String adminToken = getAuthToken(admin.getEmail(), password);
getClient(adminToken).perform(get("/api/versioning/versions/" + version.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(VersionMatcher.matchEntry(version))));
}
@Test
public void findOneSubmitterNameVisisbleTest() throws Exception {
String adminToken = getAuthToken(admin.getEmail(), password);
getClient(adminToken).perform(get("/api/versioning/versions/" + version.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(VersionMatcher.matchEntry(version))))
.andExpect(jsonPath("$.submitterName", Matchers.is(version.getEPerson().getFullName())));
}
@Test
public void findOneSubmitterNameConfigurationPropertyFalseAdminUserLinkVisibleTest() throws Exception {
configurationService.setProperty("versioning.item.history.include.submitter", false);
String adminToken = getAuthToken(admin.getEmail(), password);
getClient(adminToken).perform(get("/api/versioning/versions/" + version.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(VersionMatcher.matchEntry(version))))
.andExpect(jsonPath("$.submitterName", Matchers.is(version.getEPerson().getFullName())));
configurationService.setProperty("versioning.item.history.include.submitter", true);
}
@Test
public void findOneSubmitterNameConfigurationPropertyTrueNormalUserLinkVisibleTest() throws Exception {
String adminToken = getAuthToken(admin.getEmail(), password);
getClient(adminToken).perform(get("/api/versioning/versions/" + version.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(VersionMatcher.matchEntry(version))))
.andExpect(jsonPath("$.submitterName", Matchers.is(version.getEPerson().getFullName())));
}
@Test
public void findOneSubmitterNameConfigurationPropertyTrueAnonUserLinkVisibleTest() throws Exception {
String adminToken = getAuthToken(admin.getEmail(), password);
getClient(adminToken).perform(get("/api/versioning/versions/" + version.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(VersionMatcher.matchEntry(version))))
.andExpect(jsonPath("$.submitterName", Matchers.is(version.getEPerson().getFullName())));
}
@Test
public void findOneSubmitterNameConfigurationPropertyFalseNormalUserLinkInvisibleTest() throws Exception {
configurationService.setProperty("versioning.item.history.include.submitter", false);
String adminToken = getAuthToken(eperson.getEmail(), password);
getClient(adminToken).perform(get("/api/versioning/versions/" + version.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(VersionMatcher.matchEntry(version))))
.andExpect(jsonPath("$.submitterName").doesNotExist());
configurationService.setProperty("versioning.item.history.include.submitter", true);
}
@Test
public void findOneUnauthorizedTest() throws Exception {
getClient().perform(get("/api/versioning/versions/" + version.getID()))
.andExpect(status().isUnauthorized());
}
@Test
public void findOneForbiddenTest() throws Exception {
configurationService.setProperty("versioning.item.history.view.admin", true);
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(get("/api/versioning/versions/" + version.getID()))
.andExpect(status().isForbidden());
configurationService.setProperty("versioning.item.history.view.admin", false);
}
@Test
public void versionForItemTest() throws Exception {
getClient().perform(get("/api/core/items/" + version.getItem().getID() + "/version"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(VersionMatcher.matchEntry(version))));
}
@Test
public void versionItemTest() throws Exception {
String adminToken = getAuthToken(admin.getEmail(), password);
getClient(adminToken).perform(get("/api/versioning/versions/" + version.getID() + "/item"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(ItemMatcher.matchItemProperties(version.getItem()))));
}
@Test
public void versionItemTestWrongId() throws Exception {
String adminToken = getAuthToken(admin.getEmail(), password);
getClient(adminToken).perform(get("/api/versioning/versions/" + ((version.getID() + 5) * 57) + "/item"))
.andExpect(status().isNotFound());
}
}

View File

@@ -453,7 +453,7 @@ public class ViewEventRestRepositoryIT extends AbstractControllerIntegrationTest
@Test
public void postTestAuthenticatedUserSucces() throws Exception {
public void postTestAuthenticatedUserSuccess() throws Exception {
context.turnOffAuthorisationSystem();
@@ -485,7 +485,7 @@ public class ViewEventRestRepositoryIT extends AbstractControllerIntegrationTest
ObjectMapper mapper = new ObjectMapper();
String token = getAuthToken(admin.getEmail(), password);
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(post("/api/statistics/viewevents")
.content(mapper.writeValueAsBytes(viewEventRest))

View File

@@ -24,7 +24,6 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import javax.ws.rs.core.MediaType;
import org.apache.commons.io.IOUtils;
@@ -34,6 +33,7 @@ import org.dspace.app.rest.builder.ClaimedTaskBuilder;
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.builder.EPersonBuilder;
import org.dspace.app.rest.builder.ItemBuilder;
import org.dspace.app.rest.builder.WorkflowItemBuilder;
import org.dspace.app.rest.builder.WorkspaceItemBuilder;
import org.dspace.app.rest.matcher.CollectionMatcher;
@@ -668,15 +668,15 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
.andExpect(status().is(200));
// a workspaceitem should exist now in the submitter workspace
getClient(tokenSubmitter).perform(get("/api/submission/workspaceitems"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.workspaceitems", Matchers.containsInAnyOrder(
WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(null, "Workflow Item 1",
"2017-10-17"))))
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/submission/workspaceitems")))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(1)));
getClient(tokenSubmitter).perform(get("/api/submission/workspaceitems/search/findBySubmitter")
.param("uuid", submitter.getID().toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.workspaceitems", Matchers.containsInAnyOrder(
WorkspaceItemMatcher.matchItemWithTitleAndDateIssued(null, "Workflow Item 1",
"2017-10-17"))))
.andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/submission/workspaceitems")))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(1)));
}
@Test
@@ -1564,6 +1564,153 @@ public class WorkflowItemRestRepositoryIT extends AbstractControllerIntegrationT
;
}
@Test
public void findByItemUuidTest() throws Exception {
context.turnOffAuthorisationSystem();
//** GIVEN **
//1. A community-collection structure with one parent community with sub-community and two collections.
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
.withWorkflowGroup(1, admin).build();
//2. a workflow item
XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1)
.withTitle("Workflow Item 1")
.withIssueDate("2017-10-17")
.withAuthor("Smith, Donald").withAuthor("Doe, John")
.withSubject("ExtraEntry")
.build();
String authToken = getAuthToken(admin.getEmail(), password);
getClient(authToken).perform(get("/api/workflow/workflowitems/search/item")
.param("uuid", String.valueOf(witem.getItem().getID())))
.andExpect(status().isOk())
.andExpect(jsonPath("$",
Matchers.is(
WorkflowItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
"Workflow Item 1", "2017-10-17", "ExtraEntry"))));
}
@Test
public void findByItemUuidMissingParameterTest() throws Exception {
context.turnOffAuthorisationSystem();
//** GIVEN **
//1. A community-collection structure with one parent community with sub-community and two collections.
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
.withWorkflowGroup(1, admin).build();
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/workflow/workflowitems/search/item"))
.andExpect(status().isBadRequest());
}
@Test
public void findByItemUuidDoesntExistTest() throws Exception {
context.turnOffAuthorisationSystem();
//** GIVEN **
//1. A community-collection structure with one parent community with sub-community and two collections.
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
.withWorkflowGroup(1, admin).build();
Item item = ItemBuilder.createItem(context, col1).build();
//2. a workspace item
XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1)
.withTitle("Workflow Item 1")
.withIssueDate("2017-10-17")
.withAuthor("Smith, Donald").withAuthor("Doe, John")
.withSubject("ExtraEntry")
.build();
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/workflow/workflowitems/search/item")
.param("uuid", String.valueOf(item.getID())))
.andExpect(status().isNoContent());
}
@Test
public void findByItemUuidForbiddenTest() throws Exception {
context.turnOffAuthorisationSystem();
context.setCurrentUser(admin);
//** GIVEN **
//1. A community-collection structure with one parent community with sub-community and two collections.
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
.withWorkflowGroup(1, admin).build();
XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1)
.withTitle("Workflow Item 1")
.withIssueDate("2017-10-17")
.withAuthor("Smith, Donald").withAuthor("Doe, John")
.withSubject("ExtraEntry")
.build();
context.restoreAuthSystemState();
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(get("/api/workflow/workflowitems/search/item")
.param("uuid", String.valueOf(witem.getItem().getID())))
.andExpect(status().isForbidden());
}
@Test
public void findByItemUuidUnAuthenticatedTest() throws Exception {
context.turnOffAuthorisationSystem();
//** GIVEN **
//1. A community-collection structure with one parent community with sub-community and two collections.
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1")
.withWorkflowGroup(1, admin).build();
XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1)
.withTitle("Workflow Item 1")
.withIssueDate("2017-10-17")
.withAuthor("Smith, Donald").withAuthor("Doe, John")
.withSubject("ExtraEntry")
.build();
getClient().perform(get("/api/workflow/workflowitems/search/item")
.param("uuid", String.valueOf(witem.getItem().getID())))
.andExpect(status().isUnauthorized());
}
@Test
public void stepEmbedTest() throws Exception {

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