Merge branch 'main' into CST-4123-RespondWith401InSteadThan403toInvalidOrExpiredJWT

This commit is contained in:
Mykhaylo
2021-05-13 15:08:55 +02:00
35 changed files with 2029 additions and 150 deletions

View File

@@ -12,6 +12,7 @@ import java.util.List;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.discovery.configuration.DiscoveryConfiguration;
import org.dspace.discovery.configuration.DiscoveryMoreLikeThisConfiguration;
import org.dspace.discovery.configuration.DiscoverySearchFilterFacet;
@@ -62,11 +63,14 @@ public interface SearchService {
* @param field the field of the filter query
* @param operator equals/notequals/notcontains/authority/notauthority
* @param value the filter query value
* @param config (nullable) the discovery configuration (if not null, field's corresponding facet.type checked to
* be standard so suffix is not added for equals operator)
* @return a filter query
* @throws SQLException if database error
* An exception that provides information on a database access error or other errors.
*/
DiscoverFilterQuery toFilterQuery(Context context, String field, String operator, String value) throws SQLException;
DiscoverFilterQuery toFilterQuery(Context context, String field, String operator, String value,
DiscoveryConfiguration config) throws SQLException;
List<Item> getRelatedItems(Context context, Item item,
DiscoveryMoreLikeThisConfiguration moreLikeThisConfiguration);

View File

@@ -60,6 +60,7 @@ import org.dspace.core.Context;
import org.dspace.core.Email;
import org.dspace.core.I18nUtil;
import org.dspace.core.LogManager;
import org.dspace.discovery.configuration.DiscoveryConfiguration;
import org.dspace.discovery.configuration.DiscoveryConfigurationParameters;
import org.dspace.discovery.configuration.DiscoveryMoreLikeThisConfiguration;
import org.dspace.discovery.configuration.DiscoverySearchFilterFacet;
@@ -1069,9 +1070,9 @@ public class SolrServiceImpl implements SearchService, IndexingService {
return new ArrayList<>(0);
}
}
@Override
public DiscoverFilterQuery toFilterQuery(Context context, String field, String operator, String value)
public DiscoverFilterQuery toFilterQuery(Context context, String field, String operator, String value,
DiscoveryConfiguration config)
throws SQLException {
DiscoverFilterQuery result = new DiscoverFilterQuery();
@@ -1081,7 +1082,14 @@ public class SolrServiceImpl implements SearchService, IndexingService {
if (operator.endsWith("equals")) {
filterQuery.append("_keyword");
final boolean isStandardField
= Optional.ofNullable(config)
.flatMap(c -> Optional.ofNullable(c.getSidebarFacet(field)))
.map(facet -> facet.getType().equals(DiscoveryConfigurationParameters.TYPE_STANDARD))
.orElse(false);
if (!isStandardField) {
filterQuery.append("_keyword");
}
} else if (operator.endsWith("authority")) {
filterQuery.append("_authority");
}

View File

@@ -20,26 +20,8 @@ public class DiscoverySortConfiguration {
public static final String SCORE = "score";
/** Attributes used for sorting of results **/
public enum SORT_ORDER {
desc,
asc
}
private DiscoverySortFieldConfiguration defaultSort = null;
private List<DiscoverySortFieldConfiguration> sortFields = new ArrayList<DiscoverySortFieldConfiguration>();
private SORT_ORDER defaultSortOrder = SORT_ORDER.desc;
public DiscoverySortFieldConfiguration getDefaultSort() {
return defaultSort;
}
public void setDefaultSort(DiscoverySortFieldConfiguration defaultSort) {
this.defaultSort = defaultSort;
}
public List<DiscoverySortFieldConfiguration> getSortFields() {
return sortFields;
}
@@ -48,14 +30,6 @@ public class DiscoverySortConfiguration {
this.sortFields = sortFields;
}
public SORT_ORDER getDefaultSortOrder() {
return defaultSortOrder;
}
public void setDefaultSortOrder(SORT_ORDER defaultSortOrder) {
this.defaultSortOrder = defaultSortOrder;
}
public DiscoverySortFieldConfiguration getSortFieldConfiguration(String sortField) {
if (StringUtils.isBlank(sortField)) {
return null;
@@ -67,10 +41,6 @@ public class DiscoverySortConfiguration {
return configuration;
}
if (defaultSort != null && StringUtils.equals(defaultSort.getMetadataField(), sortField)) {
return defaultSort;
}
for (DiscoverySortFieldConfiguration sortFieldConfiguration : CollectionUtils.emptyIfNull(sortFields)) {
if (StringUtils.equals(sortFieldConfiguration.getMetadataField(), sortField)) {
return sortFieldConfiguration;

View File

@@ -18,11 +18,18 @@ public class DiscoverySortFieldConfiguration {
private String metadataField;
private String type = DiscoveryConfigurationParameters.TYPE_TEXT;
/** Attributes used for sorting of results **/
public enum SORT_ORDER {
desc,
asc
}
private SORT_ORDER defaultSortOrder;
public String getMetadataField() {
return metadataField;
}
@Autowired(required = true)
public void setMetadataField(String metadataField) {
this.metadataField = metadataField;
}
@@ -35,6 +42,15 @@ public class DiscoverySortFieldConfiguration {
this.type = type;
}
public SORT_ORDER getDefaultSortOrder() {
return defaultSortOrder;
}
@Autowired(required = true)
public void setDefaultSortOrder(SORT_ORDER defaultSortOrder) {
this.defaultSortOrder = defaultSortOrder;
}
@Override
public boolean equals(Object obj) {
if (obj != null && obj instanceof DiscoverySortFieldConfiguration) {

View File

@@ -0,0 +1,74 @@
/**
* 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.Objects;
import java.util.UUID;
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.authorize.service.AuthorizeService;
import org.dspace.content.Item;
import org.dspace.content.service.ItemService;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* The create version feature. It can be used to verify if the user can create the version of an Item.
*
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
*/
@Component
@AuthorizationFeatureDocumentation(name = CanCreateVersionFeature.NAME,
description = "It can be used to verify if the user can create a new version of an Item")
public class CanCreateVersionFeature implements AuthorizationFeature {
public static final String NAME = "canCreateVersion";
@Autowired
private ConfigurationService configurationService;
@Autowired
private AuthorizeService authorizeService;
@Autowired
private ItemService itemService;
@Override
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
if (object instanceof ItemRest) {
EPerson currentUser = context.getCurrentUser();
if (Objects.isNull(currentUser)) {
return false;
}
if (authorizeService.isAdmin(context)) {
return true;
}
if (configurationService.getBooleanProperty("versioning.submitterCanCreateNewVersion")) {
Item item = itemService.find(context, UUID.fromString(((ItemRest) object).getUuid()));
EPerson submitter = item.getSubmitter();
return Objects.nonNull(submitter) && currentUser.getID().equals(submitter.getID());
}
}
return false;
}
@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.authorize.service.AuthorizeService;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* The manageBitstreamBundles feature. It can be used to verify
* if the user can manage (ADD | REMOVE) the bundles of bitstreams of an Item.
*
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
*/
@Component
@AuthorizationFeatureDocumentation(name = CanManageBitstreamBundlesFeature.NAME,
description = "It can be used to verify if the user can manage (ADD | REMOVE) the bundles of bitstreams of an Item")
public class CanManageBitstreamBundlesFeature implements AuthorizationFeature {
public static final String NAME = "canManageBitstreamBundles";
@Autowired
private AuthorizeService authorizeService;
@Autowired
private Utils utils;
@Override
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
if (object instanceof ItemRest) {
DSpaceObject dSpaceObject = (DSpaceObject) utils.getDSpaceAPIObjectFromRest(context, object);
boolean hasRemovePermission = authorizeService.authorizeActionBoolean(context, context.getCurrentUser(),
dSpaceObject, Constants.REMOVE, true);
boolean hasAddPermission = authorizeService.authorizeActionBoolean(context, context.getCurrentUser(),
dSpaceObject, Constants.ADD, true);
return (hasRemovePermission && hasAddPermission);
}
return false;
}
@Override
public String[] getSupportedTypes() {
return new String[]{
ItemRest.CATEGORY + "." + ItemRest.NAME
};
}
}

View File

@@ -8,16 +8,25 @@
package org.dspace.app.rest.authorization.impl;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
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.CollectionRest;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Collection;
import org.dspace.content.Item;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.ItemService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.discovery.SearchServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -27,11 +36,11 @@ import org.springframework.stereotype.Component;
* Authorization is granted if the current user has ADD and WRITE permissions on the given Collection.
*/
@Component
@AuthorizationFeatureDocumentation(name = ManageMappedItemsFeature.NAME,
@AuthorizationFeatureDocumentation(name = CanManageMappingsFeature.NAME,
description = "It can be used to verify if mapped items can be listed, searched, added and removed")
public class ManageMappedItemsFeature implements AuthorizationFeature {
public class CanManageMappingsFeature implements AuthorizationFeature {
public final static String NAME = "canManageMappedItems";
public final static String NAME = "canManageMappings";
@Autowired
private AuthorizeService authorizeService;
@@ -39,6 +48,12 @@ public class ManageMappedItemsFeature implements AuthorizationFeature {
@Autowired
private Utils utils;
@Autowired
private ItemService itemService;
@Autowired
private CollectionService collectionService;
@Override
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
if (object instanceof CollectionRest) {
@@ -49,13 +64,26 @@ public class ManageMappedItemsFeature implements AuthorizationFeature {
return true;
}
}
if (object instanceof ItemRest) {
Item item = itemService.find(context, UUID.fromString(((ItemRest) object).getUuid()));
try {
List<Collection> collections = collectionService.findCollectionsWithSubmit("", context, null, 0, 2)
.stream()
.filter(c -> !c.getID().equals(item.getOwningCollection().getID()))
.collect(Collectors.toList());
return CollectionUtils.isNotEmpty(collections);
} catch (SearchServiceException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
return false;
}
@Override
public String[] getSupportedTypes() {
return new String[]{
CollectionRest.CATEGORY + "." + CollectionRest.NAME
CollectionRest.CATEGORY + "." + CollectionRest.NAME,
ItemRest.CATEGORY + "." + ItemRest.NAME
};
}
}

View File

@@ -0,0 +1,58 @@
/**
* 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.authorize.service.AuthorizeService;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* The CanManageRelationshipsFeature feature. It can be used to verify
* if the user has WRITE permission on the Item.
*
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
*/
@Component
@AuthorizationFeatureDocumentation(name = CanManageRelationshipsFeature.NAME,
description = "It can be used to verify if the user has permissions to manage relationships of the Item")
public class CanManageRelationshipsFeature implements AuthorizationFeature {
public static final String NAME = "canManageRelationships";
@Autowired
private AuthorizeService authorizeService;
@Autowired
private Utils utils;
@Override
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
if (object instanceof ItemRest) {
DSpaceObject dSpaceObject = (DSpaceObject) utils.getDSpaceAPIObjectFromRest(context, object);
return authorizeService.authorizeActionBoolean(context, context.getCurrentUser(),
dSpaceObject, Constants.WRITE, true);
}
return false;
}
@Override
public String[] getSupportedTypes() {
return new String[]{
ItemRest.CATEGORY + "." + ItemRest.NAME
};
}
}

View File

@@ -0,0 +1,75 @@
/**
* 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.Objects;
import java.util.UUID;
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.authorize.service.AuthorizeService;
import org.dspace.content.Item;
import org.dspace.content.service.ItemService;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* The manage versions feature. It can be used to verify
* if the user can create/delete or update the version of an Item.
*
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
*/
@Component
@AuthorizationFeatureDocumentation(name = CanManageVersionsFeature.NAME,
description = "It can be used to verify if the user can create/delete or update the version of an Item")
public class CanManageVersionsFeature implements AuthorizationFeature {
public static final String NAME = "canManageVersions";
@Autowired
private ConfigurationService configurationService;
@Autowired
private AuthorizeService authorizeService;
@Autowired
private ItemService itemService;
@Override
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
if (object instanceof ItemRest) {
EPerson currentUser = context.getCurrentUser();
if (Objects.isNull(currentUser)) {
return false;
}
if (authorizeService.isAdmin(context)) {
return true;
}
if (configurationService.getBooleanProperty("versioning.submitterCanCreateNewVersion")) {
Item item = itemService.find(context, UUID.fromString(((ItemRest) object).getUuid()));
EPerson submitter = item.getSubmitter();
return Objects.nonNull(submitter) && currentUser.getID().equals(submitter.getID());
}
}
return false;
}
@Override
public String[] getSupportedTypes() {
return new String[]{
ItemRest.CATEGORY + "." + ItemRest.NAME
};
}
}

View File

@@ -27,11 +27,11 @@ import org.springframework.stereotype.Component;
* current user is the object's admin. Otherwise, authorization is granted if the current user can view the object.
*/
@Component
@AuthorizationFeatureDocumentation(name = ViewVersionsFeature.NAME,
@AuthorizationFeatureDocumentation(name = CanSeeVersionsFeature.NAME,
description = "It can be used to verify if the user can view the versions of an Item")
public class ViewVersionsFeature implements AuthorizationFeature {
public class CanSeeVersionsFeature implements AuthorizationFeature {
public final static String NAME = "canViewVersions";
public final static String NAME = "canSeeVersions";
@Autowired
private ConfigurationService configurationService;

View File

@@ -11,6 +11,7 @@ import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.dspace.app.rest.model.SearchConfigurationRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.discovery.configuration.DiscoveryConfiguration;
@@ -36,7 +37,6 @@ public class DiscoverConfigurationConverter
addSearchFilters(searchConfigurationRest,
configuration.getSearchFilters(), configuration.getSidebarFacets());
addSortOptions(searchConfigurationRest, configuration.getSearchSortConfiguration());
setDefaultSortOption(configuration, searchConfigurationRest);
}
return searchConfigurationRest;
}
@@ -46,23 +46,6 @@ public class DiscoverConfigurationConverter
return DiscoveryConfiguration.class;
}
private void setDefaultSortOption(DiscoveryConfiguration configuration,
SearchConfigurationRest searchConfigurationRest) {
String defaultSort = configuration.getSearchSortConfiguration().SCORE;
if (configuration.getSearchSortConfiguration() != null) {
DiscoverySortFieldConfiguration discoverySortFieldConfiguration = configuration.getSearchSortConfiguration()
.getSortFieldConfiguration(
defaultSort);
if (discoverySortFieldConfiguration != null) {
SearchConfigurationRest.SortOption sortOption = new SearchConfigurationRest.SortOption();
sortOption.setName(discoverySortFieldConfiguration.getMetadataField());
sortOption.setActualName(discoverySortFieldConfiguration.getType());
searchConfigurationRest.addSortOption(sortOption);
}
}
}
public void addSearchFilters(SearchConfigurationRest searchConfigurationRest,
List<DiscoverySearchFilter> searchFilterList,
List<DiscoverySearchFilterFacet> facetList) {
@@ -88,8 +71,13 @@ public class DiscoverConfigurationConverter
for (DiscoverySortFieldConfiguration discoverySearchSortConfiguration : CollectionUtils
.emptyIfNull(searchSortConfiguration.getSortFields())) {
SearchConfigurationRest.SortOption sortOption = new SearchConfigurationRest.SortOption();
sortOption.setName(discoverySearchSortConfiguration.getMetadataField());
if (StringUtils.isBlank(discoverySearchSortConfiguration.getMetadataField())) {
sortOption.setName(DiscoverySortConfiguration.SCORE);
} else {
sortOption.setName(discoverySearchSortConfiguration.getMetadataField());
}
sortOption.setActualName(discoverySearchSortConfiguration.getType());
sortOption.setSortOrder(discoverySearchSortConfiguration.getDefaultSortOrder().name());
searchConfigurationRest.addSortOption(sortOption);
}
}

View File

@@ -116,6 +116,14 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH
HttpStatus.UNPROCESSABLE_ENTITY.value());
}
@ExceptionHandler( {InvalidSearchRequestException.class})
protected void handleInvalidSearchRequestException(HttpServletRequest request, HttpServletResponse response,
Exception ex) throws IOException {
sendErrorResponse(request, response, null,
"Invalid search request",
HttpStatus.UNPROCESSABLE_ENTITY.value());
}
/**
* Add user-friendly error messages to the response body for selected errors.
* Since the error messages will be exposed to the API user, the exception classes are expected to implement

View File

@@ -0,0 +1,28 @@
/**
* 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.exception;
import org.dspace.app.rest.utils.DiscoverQueryBuilder;
/**
* This exception is thrown when the given search configuration
* passed to {@link DiscoverQueryBuilder} is invalid
*
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
*/
public class InvalidSearchRequestException extends RuntimeException {
public InvalidSearchRequestException(String message, Throwable cause) {
super(message, cause);
}
public InvalidSearchRequestException(String message) {
super(message);
}
}

View File

@@ -247,6 +247,7 @@ public class SearchConfigurationRest extends BaseObjectRest<String> {
@JsonIgnore
private String actualName;
private String name;
private String sortOrder;
public void setActualName(String name) {
this.actualName = name;
@@ -264,6 +265,14 @@ public class SearchConfigurationRest extends BaseObjectRest<String> {
return name;
}
public String getSortOrder() {
return sortOrder;
}
public void setSortOrder(String sortOrder) {
this.sortOrder = sortOrder;
}
@Override
public boolean equals(Object object) {
return (object instanceof SearchConfigurationRest.SortOption &&

View File

@@ -12,6 +12,7 @@ import java.util.Map;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.dspace.app.rest.utils.URLUtils;
import org.dspace.app.rest.utils.Utils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.web.util.UriComponentsBuilder;
@@ -102,6 +103,8 @@ public class EmbeddedPageHeader {
if (page != null) {
// replace existing page & size params (if exist), otherwise append them
uriComp = uriComp.replaceQueryParam("page", page);
}
if (size != Utils.DEFAULT_PAGE_SIZE) {
uriComp = uriComp.replaceQueryParam("size", size);
}
return new Href(uriComp.build().toUriString());

View File

@@ -190,22 +190,22 @@ public class MetadataFieldRestRepository extends DSpaceRestRepository<MetadataFi
"forming schema.element.qualifier metadata field name");
}
filterQueries.add(searchService.toFilterQuery(context, MetadataFieldIndexFactoryImpl.FIELD_NAME_VARIATIONS,
OPERATOR_EQUALS, query).getFilterQuery() + "*");
OPERATOR_EQUALS, query, null).getFilterQuery() + "*");
}
if (StringUtils.isNotBlank(schemaName)) {
filterQueries.add(
searchService.toFilterQuery(context, MetadataFieldIndexFactoryImpl.SCHEMA_FIELD_NAME, OPERATOR_EQUALS,
schemaName).getFilterQuery());
schemaName, null).getFilterQuery());
}
if (StringUtils.isNotBlank(elementName)) {
filterQueries.add(
searchService.toFilterQuery(context, MetadataFieldIndexFactoryImpl.ELEMENT_FIELD_NAME, OPERATOR_EQUALS,
elementName).getFilterQuery());
elementName, null).getFilterQuery());
}
if (StringUtils.isNotBlank(qualifierName)) {
filterQueries.add(searchService
.toFilterQuery(context, MetadataFieldIndexFactoryImpl.QUALIFIER_FIELD_NAME, OPERATOR_EQUALS,
qualifierName).getFilterQuery());
qualifierName, null).getFilterQuery());
}
DiscoverQuery discoverQuery = new DiscoverQuery();

View File

@@ -14,12 +14,14 @@ import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.app.rest.converter.query.SearchQueryConverter;
import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.exception.InvalidSearchRequestException;
import org.dspace.app.rest.parameter.SearchFilter;
import org.dspace.core.Context;
import org.dspace.core.LogManager;
@@ -292,6 +294,11 @@ public class DiscoverQueryBuilder implements InitializingBean {
}
}
if (StringUtils.isNotBlank(sortBy) && !isConfigured(sortBy, searchSortConfiguration)) {
throw new InvalidSearchRequestException(
"The field: " + sortBy + "is not configured for the configuration!");
}
//Load defaults if we did not receive values
if (sortBy == null) {
sortBy = getDefaultSortField(searchSortConfiguration);
@@ -321,10 +328,14 @@ public class DiscoverQueryBuilder implements InitializingBean {
}
}
private boolean isConfigured(String sortBy, DiscoverySortConfiguration searchSortConfiguration) {
return Objects.nonNull(searchSortConfiguration.getSortFieldConfiguration(sortBy));
}
private String getDefaultSortDirection(DiscoverySortConfiguration searchSortConfiguration, String sortOrder) {
if (searchSortConfiguration != null) {
sortOrder = searchSortConfiguration.getDefaultSortOrder()
.toString();
if (Objects.nonNull(searchSortConfiguration.getSortFields()) &&
!searchSortConfiguration.getSortFields().isEmpty()) {
sortOrder = searchSortConfiguration.getSortFields().get(0).getDefaultSortOrder().name();
}
return sortOrder;
}
@@ -332,8 +343,12 @@ public class DiscoverQueryBuilder implements InitializingBean {
private String getDefaultSortField(DiscoverySortConfiguration searchSortConfiguration) {
String sortBy;// Attempt to find the default one, if none found we use SCORE
sortBy = "score";
if (searchSortConfiguration != null && searchSortConfiguration.getDefaultSort() != null) {
DiscoverySortFieldConfiguration defaultSort = searchSortConfiguration.getDefaultSort();
if (Objects.nonNull(searchSortConfiguration.getSortFields()) &&
!searchSortConfiguration.getSortFields().isEmpty()) {
DiscoverySortFieldConfiguration defaultSort = searchSortConfiguration.getSortFields().get(0);
if (StringUtils.isBlank(defaultSort.getMetadataField())) {
return sortBy;
}
sortBy = defaultSort.getMetadataField();
}
return sortBy;
@@ -378,7 +393,8 @@ public class DiscoverQueryBuilder implements InitializingBean {
DiscoverFilterQuery filterQuery = searchService.toFilterQuery(context,
filter.getIndexFieldName(),
searchFilter.getOperator(),
searchFilter.getValue());
searchFilter.getValue(),
discoveryConfiguration);
if (filterQuery != null) {
filterQueries.add(filterQuery.getFilterQuery());

View File

@@ -108,7 +108,7 @@ public class Utils {
/**
* The default page size, if unspecified in the request.
*/
private static final int DEFAULT_PAGE_SIZE = 20;
public static final int DEFAULT_PAGE_SIZE = 20;
/**
* The maximum number of embed levels to allow.

View File

@@ -2478,4 +2478,118 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
getClient().perform(get("/api/core/communities/search/findAdminAuthorized"))
.andExpect(status().isUnauthorized());
}
@Test
public void findAllSearchTopEmbeddedPaginationTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.withLogo("ThisIsSomeDummyText")
.build();
Collection col = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 2").build();
CommunityBuilder.createCommunity(context)
.withName("Parent Community 2")
.withLogo("SomeTest").build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community").build();
Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community 2").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/core/communities/search/top")
.param("size", "1")
.param("embed", "subcommunities")
.param("embed", "collections")
.param("embed.size", "subcommunities=1")
.param("embed.size", "collections=1"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.communities", Matchers.contains(
CommunityMatcher.matchCommunity(parentCommunity))))
// Verify subcommunities
.andExpect(jsonPath("$._embedded.communities[0]._embedded.subcommunities._embedded.subcommunities",
Matchers.contains(CommunityMatcher.matchCommunity(child1))))
.andExpect(jsonPath("$._embedded.communities[0]._embedded.subcommunities._links.self.href",
Matchers.containsString("/api/core/communities/" + parentCommunity.getID()
+ "/subcommunities?size=1")))
.andExpect(jsonPath("$._embedded.communities[0]._embedded.subcommunities._links.next.href",
Matchers.containsString("/api/core/communities/" + parentCommunity.getID()
+ "/subcommunities?page=1&size=1")))
.andExpect(jsonPath("$._embedded.communities[0]._embedded.subcommunities._links.last.href",
Matchers.containsString("/api/core/communities/" + parentCommunity.getID()
+ "/subcommunities?page=1&size=1")))
// Verify collections
.andExpect(jsonPath("$._embedded.communities[0]._embedded.collections._embedded.collections",
Matchers.contains(CollectionMatcher.matchCollection(col))))
.andExpect(jsonPath("$._embedded.communities[0]._embedded.collections._links.self.href",
Matchers.containsString("/api/core/communities/" + parentCommunity.getID()
+ "/collections?size=1")))
.andExpect(jsonPath("$._embedded.communities[0]._embedded.collections._links.next.href",
Matchers.containsString("/api/core/communities/" + parentCommunity.getID()
+ "/collections?page=1&size=1")))
.andExpect(jsonPath("$._embedded.communities[0]._embedded.collections._links.last.href",
Matchers.containsString("/api/core/communities/" + parentCommunity.getID()
+ "/collections?page=1&size=1")))
.andExpect(jsonPath("$._links.self.href",
Matchers.containsString("/api/core/communities/search/top?size=1")))
.andExpect(jsonPath("$._links.first.href",
Matchers.containsString("/api/core/communities/search/top?page=0&size=1")))
.andExpect(jsonPath("$._links.next.href",
Matchers.containsString("/api/core/communities/search/top?page=1&size=1")))
.andExpect(jsonPath("$._links.last.href",
Matchers.containsString("/api/core/communities/search/top?page=1&size=1")))
.andExpect(jsonPath("$.page.size", is(1)))
.andExpect(jsonPath("$.page.totalPages", is(2)))
.andExpect(jsonPath("$.page.totalElements", is(2)));
getClient().perform(get("/api/core/communities/search/top")
.param("size", "1")
.param("embed", "subcommunities")
.param("embed", "collections")
.param("embed.size", "subcommunities=2")
.param("embed.size", "collections=2"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.communities", Matchers.contains(
CommunityMatcher.matchCommunity(parentCommunity))))
// Verify subcommunities
.andExpect(jsonPath("$._embedded.communities[0]._embedded.subcommunities._embedded.subcommunities",
Matchers.containsInAnyOrder(CommunityMatcher.matchCommunity(child1),
CommunityMatcher.matchCommunity(child2)
)))
.andExpect(jsonPath("$._embedded.communities[0]._embedded.subcommunities._links.self.href",
Matchers.containsString("/api/core/communities/" + parentCommunity.getID()
+ "/subcommunities?size=2")))
// Verify collections
.andExpect(jsonPath("$._embedded.communities[0]._embedded.collections._embedded.collections",
Matchers.containsInAnyOrder(CollectionMatcher.matchCollection(col),
CollectionMatcher.matchCollection(col2)
)))
.andExpect(jsonPath("$._embedded.communities[0]._embedded.collections._links.self.href",
Matchers.containsString("/api/core/communities/" + parentCommunity.getID()
+ "/collections?size=2")))
.andExpect(jsonPath("$._links.self.href",
Matchers.containsString("/api/core/communities/search/top?size=1")))
.andExpect(jsonPath("$._links.first.href",
Matchers.containsString("/api/core/communities/search/top?page=0&size=1")))
.andExpect(jsonPath("$._links.next.href",
Matchers.containsString("/api/core/communities/search/top?page=1&size=1")))
.andExpect(jsonPath("$._links.last.href",
Matchers.containsString("/api/core/communities/search/top?page=1&size=1")))
.andExpect(jsonPath("$.page.size", is(1)))
.andExpect(jsonPath("$.page.totalPages", is(2)))
.andExpect(jsonPath("$.page.totalElements", is(2)));
}
}

View File

@@ -55,6 +55,7 @@ import org.dspace.content.Item;
import org.dspace.content.WorkspaceItem;
import org.dspace.content.authority.Choices;
import org.dspace.content.authority.service.MetadataAuthorityService;
import org.dspace.discovery.configuration.DiscoverySortFieldConfiguration;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.services.ConfigurationService;
@@ -988,14 +989,74 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
SearchFilterMatcher.isJournalOfPublicationRelation()
)))
//These sortOptions need to be present as it's the default in the configuration
.andExpect(jsonPath("$.sortOptions", containsInAnyOrder(
SortOptionMatcher.titleSortOption(),
SortOptionMatcher.dateIssuedSortOption(),
SortOptionMatcher.dateAccessionedSortOption(),
SortOptionMatcher.scoreSortOption()
.andExpect(jsonPath("$.sortOptions", contains(
SortOptionMatcher.sortOptionMatcher(
"score", DiscoverySortFieldConfiguration.SORT_ORDER.desc.name()),
SortOptionMatcher.sortOptionMatcher(
"dc.title", DiscoverySortFieldConfiguration.SORT_ORDER.asc.name()),
SortOptionMatcher.sortOptionMatcher(
"dc.date.issued", DiscoverySortFieldConfiguration.SORT_ORDER.desc.name()),
SortOptionMatcher.sortOptionMatcher(
"dc.date.accessioned", DiscoverySortFieldConfiguration.SORT_ORDER.desc.name())
)));
}
@Test
public void checkSortOrderInPersonOrOrgunitConfigurationTest() throws Exception {
getClient().perform(get("/api/discover/search")
.param("configuration", "personOrOrgunit"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.type", is("discover")))
.andExpect(jsonPath("$._links.objects.href", containsString("api/discover/search/objects")))
.andExpect(jsonPath("$._links.self.href", containsString("api/discover/search")))
.andExpect(jsonPath("$.sortOptions", contains(
SortOptionMatcher.sortOptionMatcher("dspace.entity.type",
DiscoverySortFieldConfiguration.SORT_ORDER.desc.name()),
SortOptionMatcher.sortOptionMatcher("organization.legalName",
DiscoverySortFieldConfiguration.SORT_ORDER.asc.name()),
SortOptionMatcher.sortOptionMatcher("organisation.address.addressCountry",
DiscoverySortFieldConfiguration.SORT_ORDER.asc.name()),
SortOptionMatcher.sortOptionMatcher("organisation.address.addressLocality",
DiscoverySortFieldConfiguration.SORT_ORDER.asc.name()),
SortOptionMatcher.sortOptionMatcher("organisation.foundingDate",
DiscoverySortFieldConfiguration.SORT_ORDER.desc.name()),
SortOptionMatcher.sortOptionMatcher("dc.date.accessioned",
DiscoverySortFieldConfiguration.SORT_ORDER.desc.name()),
SortOptionMatcher.sortOptionMatcher("person.familyName",
DiscoverySortFieldConfiguration.SORT_ORDER.asc.name()),
SortOptionMatcher.sortOptionMatcher("person.givenName",
DiscoverySortFieldConfiguration.SORT_ORDER.asc.name()),
SortOptionMatcher.sortOptionMatcher("person.birthDate",
DiscoverySortFieldConfiguration.SORT_ORDER.desc.name())
)));
}
@Test
public void discoverSearchByFieldNotConfiguredTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 1")
.build();
ItemBuilder.createItem(context, col1)
.withTitle("Test")
.withIssueDate("2010-10-17")
.withAuthor("Testing, Works")
.withSubject("ExtraEntry").build();
context.restoreAuthSystemState();
getClient().perform(get("/api/discover/search/objects")
.param("sort", "dc.date.accessioned, ASC")
.param("configuration", "workspace"))
.andExpect(status().isUnprocessableEntity());
}
@Test
public void discoverSearchObjectsTest() throws Exception {
@@ -5177,6 +5238,137 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
.andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")));
}
@Test
public void discoverSearchObjectsTestForAdministrativeViewWithFiltersEquals() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder
.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder
.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder
.createCollection(context, child1)
.withName("Collection 1")
.build();
Collection col2 = CollectionBuilder
.createCollection(context, child1)
.withName("Collection 2")
.build();
ItemBuilder.createItem(context, col1)
.withTitle("Public Test Item")
.withIssueDate("2010-10-17")
.withAuthor("Smith, Donald")
.withSubject("ExtraEntry")
.build();
ItemBuilder.createItem(context, col2)
.withTitle("Withdrawn Test Item")
.withIssueDate("1990-02-13")
.withAuthor("Smith, Maria")
.withAuthor("Doe, Jane")
.withSubject("ExtraEntry")
.withdrawn()
.build();
ItemBuilder.createItem(context, col2)
.withTitle("Private Test Item")
.withIssueDate("2010-02-13")
.withAuthor("Smith, Maria")
.withAuthor("Doe, Jane")
.withSubject("AnotherTest")
.withSubject("ExtraEntry")
.makeUnDiscoverable()
.build();
context.restoreAuthSystemState();
String adminToken = getAuthToken(admin.getEmail(), password);
getClient(adminToken)
.perform(get("/api/discover/search/objects")
.param("configuration", "administrativeView")
.param("query", "Test")
.param("f.withdrawn", "true,equals")
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.type", is("discover")))
.andExpect(jsonPath("$._embedded.searchResult.page", is(
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 1)
)))
.andExpect(jsonPath("$._embedded.searchResult._embedded.objects",
Matchers.contains(
SearchResultMatcher.matchOnItemName("item", "items", "Withdrawn Test Item")
)
))
.andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")));
getClient(adminToken)
.perform(get("/api/discover/search/objects")
.param("configuration", "administrativeView")
.param("query", "Test")
.param("f.withdrawn", "false,equals")
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.type", is("discover")))
.andExpect(jsonPath("$._embedded.searchResult.page", is(
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 2)
)))
.andExpect(jsonPath("$._embedded.searchResult._embedded.objects",
Matchers.containsInAnyOrder(
SearchResultMatcher.matchOnItemName("item", "items", "Public Test Item"),
SearchResultMatcher.matchOnItemName("item", "items", "Private Test Item")
)
))
.andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")));
getClient(adminToken)
.perform(get("/api/discover/search/objects")
.param("configuration", "administrativeView")
.param("query", "Test")
.param("f.discoverable", "true,equals")
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.type", is("discover")))
.andExpect(jsonPath("$._embedded.searchResult.page", is(
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 2)
)))
.andExpect(jsonPath("$._embedded.searchResult._embedded.objects",
Matchers.containsInAnyOrder(
SearchResultMatcher.matchOnItemName("item", "items", "Public Test Item"),
SearchResultMatcher.matchOnItemName("item", "items", "Withdrawn Test Item")
)
))
.andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")));
getClient(adminToken)
.perform(get("/api/discover/search/objects")
.param("configuration", "administrativeView")
.param("query", "Test")
.param("f.discoverable", "false,equals")
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.type", is("discover")))
.andExpect(jsonPath("$._embedded.searchResult.page", is(
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 1)
)))
.andExpect(jsonPath("$._embedded.searchResult._embedded.objects",
Matchers.contains(
SearchResultMatcher.matchOnItemName("item", "items", "Private Test Item")
)
))
.andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")));
}
@Test
public void discoverSearchPoolTaskObjectsTest() throws Exception {
context.turnOffAuthorisationSystem();

View File

@@ -0,0 +1,219 @@
/**
* 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 static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.dspace.app.rest.authorization.impl.CanCreateVersionFeature;
import org.dspace.app.rest.converter.ItemConverter;
import org.dspace.app.rest.matcher.AuthorizationMatcher;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.projection.DefaultProjection;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.app.rest.utils.Utils;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.EPersonBuilder;
import org.dspace.builder.ItemBuilder;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.eperson.EPerson;
import org.dspace.services.ConfigurationService;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Test for the canCreateVersion authorization feature.
*
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
*/
public class CanCreateVersionFeatureIT extends AbstractControllerIntegrationTest {
@Autowired
private Utils utils;
@Autowired
private ItemConverter itemConverter;
@Autowired
private ConfigurationService configurationService;
@Autowired
private AuthorizationFeatureService authorizationFeatureService;
private Item itemA;
private Item itemB;
private EPerson user;
private ItemRest itemARest;
private Community communityA;
private Collection collectionA;
private AuthorizationFeature canCreateVersionFeature;
final String feature = "canCreateVersion";
@Override
@Before
public void setUp() throws Exception {
super.setUp();
context.turnOffAuthorisationSystem();
canCreateVersionFeature = authorizationFeatureService.find(CanCreateVersionFeature.NAME);
user = EPersonBuilder.createEPerson(context)
.withEmail("userEmail@test.com")
.withPassword(password).build();
communityA = CommunityBuilder.createCommunity(context)
.withName("communityA").build();
collectionA = CollectionBuilder.createCollection(context, communityA)
.withName("collectionA").build();
itemA = ItemBuilder.createItem(context, collectionA)
.withTitle("Item A").build();
itemB = ItemBuilder.createItem(context, collectionA)
.withTitle("Item B").build();
context.restoreAuthSystemState();
itemARest = itemConverter.convert(itemA, Projection.DEFAULT);
}
@Test
public void anonymousHasNotAccessTest() throws Exception {
getClient().perform(get("/api/authz/authorizations/search/object")
.param("embed", "feature")
.param("feature", feature)
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.totalElements", is(0)))
.andExpect(jsonPath("$._embedded").doesNotExist());
}
@Test
public void epersonHasNotAccessTest() throws Exception {
String epersonToken = getAuthToken(eperson.getEmail(), password);
getClient(epersonToken).perform(get("/api/authz/authorizations/search/object")
.param("embed", "feature")
.param("feature", feature)
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.totalElements", is(0)))
.andExpect(jsonPath("$._embedded").doesNotExist());
}
@Test
public void adminItemSuccessTest() throws Exception {
String adminToken = getAuthToken(admin.getEmail(), password);
getClient(adminToken).perform(get("/api/authz/authorizations/search/object")
.param("embed", "feature")
.param("feature", feature)
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.totalElements", greaterThan(0)))
.andExpect(jsonPath("$._embedded").exists());
}
@Test
public void submitterItemSuccessTest() throws Exception {
context.turnOffAuthorisationSystem();
configurationService.setProperty("versioning.submitterCanCreateNewVersion", true);
itemA.setSubmitter(user);
context.restoreAuthSystemState();
String userToken = getAuthToken(user.getEmail(), password);
getClient(userToken).perform(get("/api/authz/authorizations/search/object")
.param("embed", "feature")
.param("feature", feature)
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.totalElements", greaterThan(0)))
.andExpect(jsonPath("$._embedded").exists());
}
@Test
public void submitterItemWithPropertySubmitterCanCreateNewVersionIsFalseTest() throws Exception {
context.turnOffAuthorisationSystem();
configurationService.setProperty("versioning.submitterCanCreateNewVersion", false);
itemA.setSubmitter(user);
context.restoreAuthSystemState();
String userToken = getAuthToken(user.getEmail(), password);
getClient(userToken).perform(get("/api/authz/authorizations/search/object")
.param("embed", "feature")
.param("feature", feature)
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.totalElements", is(0)))
.andExpect(jsonPath("$._embedded").doesNotExist());
}
@Test
@SuppressWarnings("unchecked")
public void checkCanCreateVersionsFeatureTest() throws Exception {
context.turnOffAuthorisationSystem();
configurationService.setProperty("versioning.submitterCanCreateNewVersion", true);
itemA.setSubmitter(user);
itemB.setSubmitter(admin);
context.restoreAuthSystemState();
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
ItemRest itemRestB = itemConverter.convert(itemB, DefaultProjection.DEFAULT);
String tokenEPerson = getAuthToken(eperson.getEmail(), password);
String tokenAdmin = getAuthToken(admin.getEmail(), password);
String tokenUser = getAuthToken(user.getEmail(), password);
// define authorizations that we know must exists
Authorization admin2ItemA = new Authorization(admin, canCreateVersionFeature, itemRestA);
Authorization admin2ItemB = new Authorization(admin, canCreateVersionFeature, itemRestB);
Authorization user2ItemA = new Authorization(user, canCreateVersionFeature, itemRestA);
// define authorization that we know not exists
Authorization eperson2ItemA = new Authorization(eperson, canCreateVersionFeature, itemRestA);
Authorization eperson2ItemB = new Authorization(eperson, canCreateVersionFeature, itemRestB);
Authorization user2ItemB = new Authorization(user, canCreateVersionFeature, itemRestB);
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemB.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemB))));
getClient(tokenUser).perform(get("/api/authz/authorizations/" + user2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(user2ItemA))));
getClient(tokenEPerson).perform(get("/api/authz/authorizations/" + eperson2ItemA.getID()))
.andExpect(status().isNotFound());
getClient(tokenEPerson).perform(get("/api/authz/authorizations/" + eperson2ItemB.getID()))
.andExpect(status().isNotFound());
getClient(tokenUser).perform(get("/api/authz/authorizations/" + user2ItemB.getID()))
.andExpect(status().isNotFound());
}
}

View File

@@ -0,0 +1,547 @@
/**
* 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 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.authorization.impl.CanManageBitstreamBundlesFeature;
import org.dspace.app.rest.converter.ItemConverter;
import org.dspace.app.rest.matcher.AuthorizationMatcher;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.projection.DefaultProjection;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.EPersonBuilder;
import org.dspace.builder.ItemBuilder;
import org.dspace.builder.ResourcePolicyBuilder;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.core.Constants;
import org.dspace.eperson.EPerson;
import org.dspace.services.ConfigurationService;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Test for the canManageBitstreamBundles authorization feature.
*
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
*/
public class CanManageBitstreamBundlesFeatureIT extends AbstractControllerIntegrationTest {
@Autowired
private ItemConverter itemConverter;
@Autowired
private AuthorizeService authorizeService;
@Autowired
private ConfigurationService configurationService;
@Autowired
private AuthorizationFeatureService authorizationFeatureService;
private Item itemA;
private Item itemB;
private EPerson userA;
private EPerson userB;
private EPerson userColAadmin;
private EPerson userColBadmin;
private EPerson userComAdmin;
private Community communityA;
private Collection collectionA;
private Collection collectionB;
private AuthorizationFeature canManageBitstreamBundlesFeature;
final String feature = "canManageBitstreamBundles";
@Before
@Override
public void setUp() throws Exception {
super.setUp();
context.turnOffAuthorisationSystem();
canManageBitstreamBundlesFeature = authorizationFeatureService.find(CanManageBitstreamBundlesFeature.NAME);
userA = EPersonBuilder.createEPerson(context)
.withEmail("userEmail@test.com")
.withPassword(password).build();
userB = EPersonBuilder.createEPerson(context)
.withEmail("userB.email@test.com")
.withPassword(password).build();
userColAadmin = EPersonBuilder.createEPerson(context)
.withEmail("userColAadmin@test.com")
.withPassword(password).build();
userColBadmin = EPersonBuilder.createEPerson(context)
.withEmail("userColBadmin@test.com")
.withPassword(password).build();
userComAdmin = EPersonBuilder.createEPerson(context)
.withEmail("userComAdmin@test.com")
.withPassword(password).build();
communityA = CommunityBuilder.createCommunity(context)
.withName("communityA")
.withAdminGroup(userComAdmin).build();
collectionA = CollectionBuilder.createCollection(context, communityA)
.withName("Collection A")
.withAdminGroup(userColAadmin).build();
collectionB = CollectionBuilder.createCollection(context, communityA)
.withName("Collection B")
.withAdminGroup(userColBadmin).build();
itemA = ItemBuilder.createItem(context, collectionA)
.withTitle("Item A").build();
itemB = ItemBuilder.createItem(context, collectionB)
.withTitle("Item B").build();
context.restoreAuthSystemState();
}
@Test
@SuppressWarnings("unchecked")
public void checkCanCreateVersionsFeatureTest() throws Exception {
context.turnOffAuthorisationSystem();
//permissions for userA
authorizeService.addPolicy(context, itemA, Constants.ADD, userA);
authorizeService.addPolicy(context, itemA, Constants.REMOVE, userA);
// permissions for userB
authorizeService.addPolicy(context, itemA, Constants.REMOVE, userB);
authorizeService.addPolicy(context, itemB, Constants.REMOVE, userB);
authorizeService.addPolicy(context, itemB, Constants.ADD, userB);
context.restoreAuthSystemState();
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
ItemRest itemRestB = itemConverter.convert(itemB, DefaultProjection.DEFAULT);
String tokenEPerson = getAuthToken(eperson.getEmail(), password);
String tokenAdmin = getAuthToken(admin.getEmail(), password);
String tokenAUser = getAuthToken(userA.getEmail(), password);
String tokenBUser = getAuthToken(userB.getEmail(), password);
String tokenComAdmin = getAuthToken(userComAdmin.getEmail(), password);
String tokenColAadmin = getAuthToken(userColAadmin.getEmail(), password);
String tokenColBadmin = getAuthToken(userColBadmin.getEmail(), password);
// define authorizations that we know must exists
Authorization admin2ItemA = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestA);
Authorization admin2ItemB = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestB);
Authorization userA2ItemA = new Authorization(userA, canManageBitstreamBundlesFeature, itemRestA);
Authorization userB2ItemB = new Authorization(userB, canManageBitstreamBundlesFeature, itemRestB);
Authorization comAdmin2ItemB = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestB);
Authorization comAdmin2ItemA = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestA);
Authorization colAadmin2ItemA = new Authorization(userColAadmin, canManageBitstreamBundlesFeature, itemRestA);
// define authorization that we know not exists
Authorization userB2ItemA = new Authorization(userB, canManageBitstreamBundlesFeature, itemRestA);
Authorization userA2ItemB = new Authorization(userA, canManageBitstreamBundlesFeature, itemRestB);
Authorization eperson2ItemA = new Authorization(eperson, canManageBitstreamBundlesFeature, itemRestA);
Authorization eperson2ItemB = new Authorization(eperson, canManageBitstreamBundlesFeature, itemRestB);
Authorization anonymous2ItemA = new Authorization(null, canManageBitstreamBundlesFeature, itemRestA);
Authorization anonymous2ItemB = new Authorization(null, canManageBitstreamBundlesFeature, itemRestB);
Authorization colAadmin2ItemB = new Authorization(userColAadmin, canManageBitstreamBundlesFeature, itemRestB);
Authorization colBadmin2ItemA = new Authorization(userColBadmin, canManageBitstreamBundlesFeature, itemRestA);
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemB.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemB))));
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(userA2ItemA))));
getClient(tokenBUser).perform(get("/api/authz/authorizations/" + userB2ItemB.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(userB2ItemB))));
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemB.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemB))));
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemA))));
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(colAadmin2ItemA))));
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemB.getID()))
.andExpect(status().isNotFound());
getClient(tokenColBadmin).perform(get("/api/authz/authorizations/" + colBadmin2ItemA.getID()))
.andExpect(status().isNotFound());
getClient(tokenBUser).perform(get("/api/authz/authorizations/" + userB2ItemA.getID()))
.andExpect(status().isNotFound());
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemB.getID()))
.andExpect(status().isNotFound());
getClient(tokenEPerson).perform(get("/api/authz/authorizations/" + eperson2ItemA.getID()))
.andExpect(status().isNotFound());
getClient(tokenEPerson).perform(get("/api/authz/authorizations/" + eperson2ItemB.getID()))
.andExpect(status().isNotFound());
getClient().perform(get("/api/authz/authorizations/" + anonymous2ItemA.getID()))
.andExpect(status().isNotFound());
getClient().perform(get("/api/authz/authorizations/" + anonymous2ItemB.getID()))
.andExpect(status().isNotFound());
}
@Test
@SuppressWarnings("unchecked")
public void itemAdminSetPropertyCreateBitstreamToFalseTest() throws Exception {
context.turnOffAuthorisationSystem();
ResourcePolicyBuilder.createResourcePolicy(context)
.withAction(Constants.ADMIN)
.withUser(userA)
.withDspaceObject(itemA).build();
configurationService.setProperty("core.authorization.item-admin.create-bitstream", false);
context.restoreAuthSystemState();
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
String tokenAdmin = getAuthToken(admin.getEmail(), password);
String tokenAUser = getAuthToken(userA.getEmail(), password);
String tokenComAdmin = getAuthToken(userComAdmin.getEmail(), password);
String tokenColAadmin = getAuthToken(userColAadmin.getEmail(), password);
// define authorizations that we know must exists
Authorization admin2ItemA = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestA);
Authorization comAdmin2ItemA = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestA);
Authorization colAadmin2ItemA = new Authorization(userColAadmin, canManageBitstreamBundlesFeature, itemRestA);
// define authorization that we know not exists
Authorization userA2ItemA = new Authorization(userA, canManageBitstreamBundlesFeature, itemRestA);
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemA))));
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(colAadmin2ItemA))));
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
.andExpect(status().isNotFound());
}
@Test
@SuppressWarnings("unchecked")
public void itemAdminSetPropertyDeleteBitstreamToFalseTest() throws Exception {
context.turnOffAuthorisationSystem();
ResourcePolicyBuilder.createResourcePolicy(context)
.withAction(Constants.ADMIN)
.withUser(userA)
.withDspaceObject(itemA).build();
configurationService.setProperty("core.authorization.item-admin.delete-bitstream", false);
context.restoreAuthSystemState();
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
String tokenAdmin = getAuthToken(admin.getEmail(), password);
String tokenAUser = getAuthToken(userA.getEmail(), password);
String tokenComAdmin = getAuthToken(userComAdmin.getEmail(), password);
String tokenColAadmin = getAuthToken(userColAadmin.getEmail(), password);
// define authorizations that we know must exists
Authorization admin2ItemA = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestA);
Authorization comAdmin2ItemA = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestA);
Authorization colAadmin2ItemA = new Authorization(userColAadmin, canManageBitstreamBundlesFeature, itemRestA);
// define authorization that we know not exists
Authorization userA2ItemA = new Authorization(userA, canManageBitstreamBundlesFeature, itemRestA);
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemA))));
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(colAadmin2ItemA))));
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
.andExpect(status().isNotFound());
}
@Test
@SuppressWarnings("unchecked")
public void itemAdminSetPropertyCollectionAdminCreateBitstreamToFalseTest() throws Exception {
context.turnOffAuthorisationSystem();
ResourcePolicyBuilder.createResourcePolicy(context)
.withAction(Constants.ADMIN)
.withUser(userA)
.withDspaceObject(itemA).build();
configurationService.setProperty("core.authorization.collection-admin.item.create-bitstream", false);
configurationService.setProperty("core.authorization.item-admin.delete-bitstream", false);
configurationService.setProperty("core.authorization.item-admin.create-bitstream", false);
context.restoreAuthSystemState();
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
ItemRest itemRestB = itemConverter.convert(itemB, DefaultProjection.DEFAULT);
String tokenAdmin = getAuthToken(admin.getEmail(), password);
String tokenAUser = getAuthToken(userA.getEmail(), password);
String tokenComAdmin = getAuthToken(userComAdmin.getEmail(), password);
String tokenColAadmin = getAuthToken(userColAadmin.getEmail(), password);
String tokenColBadmin = getAuthToken(userColBadmin.getEmail(), password);
// define authorizations that we know must exists
Authorization admin2ItemA = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestA);
Authorization admin2ItemB = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestB);
Authorization comAdmin2ItemA = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestA);
Authorization comAdmin2ItemB = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestB);
// define authorization that we know not exists
Authorization colAadmin2ItemA = new Authorization(userColAadmin, canManageBitstreamBundlesFeature, itemRestA);
Authorization colBadmin2ItemB = new Authorization(userColBadmin, canManageBitstreamBundlesFeature, itemRestA);
Authorization userA2ItemA = new Authorization(userA, canManageBitstreamBundlesFeature, itemRestA);
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemB.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemB))));
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemA))));
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemB.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemB))));
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemA.getID()))
.andExpect(status().isNotFound());
getClient(tokenColBadmin).perform(get("/api/authz/authorizations/" + colBadmin2ItemB.getID()))
.andExpect(status().isNotFound());
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
.andExpect(status().isNotFound());
}
@Test
@SuppressWarnings("unchecked")
public void itemAdminSetPropertyCollectionAdminDeleteBitstreamToFalseTest() throws Exception {
context.turnOffAuthorisationSystem();
ResourcePolicyBuilder.createResourcePolicy(context)
.withAction(Constants.ADMIN)
.withUser(userA)
.withDspaceObject(itemA).build();
configurationService.setProperty("core.authorization.collection-admin.item.delete-bitstream", false);
configurationService.setProperty("core.authorization.item-admin.delete-bitstream", false);
configurationService.setProperty("core.authorization.item-admin.create-bitstream", false);
context.restoreAuthSystemState();
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
ItemRest itemRestB = itemConverter.convert(itemB, DefaultProjection.DEFAULT);
String tokenAdmin = getAuthToken(admin.getEmail(), password);
String tokenAUser = getAuthToken(userA.getEmail(), password);
String tokenComAdmin = getAuthToken(userComAdmin.getEmail(), password);
String tokenColAadmin = getAuthToken(userColAadmin.getEmail(), password);
String tokenColBadmin = getAuthToken(userColBadmin.getEmail(), password);
// define authorizations that we know must exists
Authorization admin2ItemA = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestA);
Authorization admin2ItemB = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestB);
Authorization comAdmin2ItemA = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestA);
Authorization comAdmin2ItemB = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestB);
// define authorization that we know not exists
Authorization colAadmin2ItemA = new Authorization(userColAadmin, canManageBitstreamBundlesFeature, itemRestA);
Authorization colBadmin2ItemB = new Authorization(userColBadmin, canManageBitstreamBundlesFeature, itemRestA);
Authorization userA2ItemA = new Authorization(userA, canManageBitstreamBundlesFeature, itemRestA);
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemB.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemB))));
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemA))));
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemB.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemB))));
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemA.getID()))
.andExpect(status().isNotFound());
getClient(tokenColBadmin).perform(get("/api/authz/authorizations/" + colBadmin2ItemB.getID()))
.andExpect(status().isNotFound());
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
.andExpect(status().isNotFound());
}
@Test
@SuppressWarnings("unchecked")
public void itemAdminSetPropertyCommunityAdminCreateBitstreamToFalseTest() throws Exception {
context.turnOffAuthorisationSystem();
ResourcePolicyBuilder.createResourcePolicy(context)
.withAction(Constants.ADMIN)
.withUser(userA)
.withDspaceObject(itemA).build();
configurationService.setProperty("core.authorization.community-admin.item.create-bitstream", false);
configurationService.setProperty("core.authorization.collection-admin.item.create-bitstream", false);
configurationService.setProperty("core.authorization.collection-admin.item.delete-bitstream", false);
configurationService.setProperty("core.authorization.item-admin.delete-bitstream", false);
configurationService.setProperty("core.authorization.item-admin.create-bitstream", false);
context.restoreAuthSystemState();
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
ItemRest itemRestB = itemConverter.convert(itemB, DefaultProjection.DEFAULT);
String tokenAdmin = getAuthToken(admin.getEmail(), password);
String tokenAUser = getAuthToken(userA.getEmail(), password);
String tokenComAdmin = getAuthToken(userComAdmin.getEmail(), password);
String tokenColAadmin = getAuthToken(userColAadmin.getEmail(), password);
String tokenColBadmin = getAuthToken(userColBadmin.getEmail(), password);
// define authorizations that we know must exists
Authorization admin2ItemA = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestA);
Authorization admin2ItemB = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestB);
// define authorization that we know not exists
Authorization comAdmin2ItemA = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestA);
Authorization comAdmin2ItemB = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestB);
Authorization colAadmin2ItemA = new Authorization(userColAadmin, canManageBitstreamBundlesFeature, itemRestA);
Authorization colBadmin2ItemB = new Authorization(userColBadmin, canManageBitstreamBundlesFeature, itemRestA);
Authorization userA2ItemA = new Authorization(userA, canManageBitstreamBundlesFeature, itemRestA);
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemB.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemB))));
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemA.getID()))
.andExpect(status().isNotFound());
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemB.getID()))
.andExpect(status().isNotFound());
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemA.getID()))
.andExpect(status().isNotFound());
getClient(tokenColBadmin).perform(get("/api/authz/authorizations/" + colBadmin2ItemB.getID()))
.andExpect(status().isNotFound());
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
.andExpect(status().isNotFound());
}
@Test
@SuppressWarnings("unchecked")
public void itemAdminSetPropertyCommunityAdminDeleteBitstreamToFalseTest() throws Exception {
context.turnOffAuthorisationSystem();
ResourcePolicyBuilder.createResourcePolicy(context)
.withAction(Constants.ADMIN)
.withUser(userA)
.withDspaceObject(itemA).build();
configurationService.setProperty("core.authorization.community-admin.item.delete-bitstream", false);
configurationService.setProperty("core.authorization.collection-admin.item.create-bitstream", false);
configurationService.setProperty("core.authorization.collection-admin.item.delete-bitstream", false);
configurationService.setProperty("core.authorization.item-admin.delete-bitstream", false);
configurationService.setProperty("core.authorization.item-admin.create-bitstream", false);
context.restoreAuthSystemState();
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
ItemRest itemRestB = itemConverter.convert(itemB, DefaultProjection.DEFAULT);
String tokenAdmin = getAuthToken(admin.getEmail(), password);
String tokenAUser = getAuthToken(userA.getEmail(), password);
String tokenComAdmin = getAuthToken(userComAdmin.getEmail(), password);
String tokenColAadmin = getAuthToken(userColAadmin.getEmail(), password);
String tokenColBadmin = getAuthToken(userColBadmin.getEmail(), password);
// define authorizations that we know must exists
Authorization admin2ItemA = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestA);
Authorization admin2ItemB = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestB);
// define authorization that we know not exists
Authorization comAdmin2ItemA = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestA);
Authorization comAdmin2ItemB = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestB);
Authorization colAadmin2ItemA = new Authorization(userColAadmin, canManageBitstreamBundlesFeature, itemRestA);
Authorization colBadmin2ItemB = new Authorization(userColBadmin, canManageBitstreamBundlesFeature, itemRestA);
Authorization userA2ItemA = new Authorization(userA, canManageBitstreamBundlesFeature, itemRestA);
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemB.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemB))));
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemA.getID()))
.andExpect(status().isNotFound());
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemB.getID()))
.andExpect(status().isNotFound());
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemA.getID()))
.andExpect(status().isNotFound());
getClient(tokenColBadmin).perform(get("/api/authz/authorizations/" + colBadmin2ItemB.getID()))
.andExpect(status().isNotFound());
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
.andExpect(status().isNotFound());
}
}

View File

@@ -17,17 +17,24 @@ import java.io.InputStream;
import org.apache.commons.codec.CharEncoding;
import org.apache.commons.io.IOUtils;
import org.dspace.app.rest.authorization.impl.CanManageMappingsFeature;
import org.dspace.app.rest.converter.BitstreamConverter;
import org.dspace.app.rest.converter.CollectionConverter;
import org.dspace.app.rest.converter.ItemConverter;
import org.dspace.app.rest.matcher.AuthorizationMatcher;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.CollectionRest;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.projection.DefaultProjection;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.builder.BitstreamBuilder;
import org.dspace.builder.BundleBuilder;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.EPersonBuilder;
import org.dspace.builder.ItemBuilder;
import org.dspace.builder.ResourcePolicyBuilder;
import org.dspace.content.Bitstream;
@@ -36,14 +43,16 @@ import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.core.Constants;
import org.dspace.eperson.EPerson;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Test for the canManageMappedItems authorization feature
* Test for the canManageMappings authorization feature.
*/
public class ManageMappedItemsFeatureIT extends AbstractControllerIntegrationTest {
public class CanManageMappingsFeatureIT extends AbstractControllerIntegrationTest {
@Autowired
private Utils utils;
@@ -54,15 +63,27 @@ public class ManageMappedItemsFeatureIT extends AbstractControllerIntegrationTes
@Autowired
private BitstreamConverter bitstreamConverter;
@Autowired
private ItemConverter itemConverter;
@Autowired
private AuthorizeService authorizeService;
@Autowired
private AuthorizationFeatureService authorizationFeatureService;
private EPerson userA;
private Community communityA;
private Collection collectionA;
private Collection collectionB;
private CollectionRest collectionARest;
private Item itemA;
private Bitstream bitstreamA;
private BitstreamRest bitstreamARest;
private Bundle bundleA;
private AuthorizationFeature canManageMappingsFeature;
final String feature = "canManageMappedItems";
final String feature = "canManageMappings";
@Override
@Before
@@ -70,12 +91,19 @@ public class ManageMappedItemsFeatureIT extends AbstractControllerIntegrationTes
super.setUp();
context.turnOffAuthorisationSystem();
userA = EPersonBuilder.createEPerson(context)
.withEmail("userEmail@test.com")
.withPassword(password).build();
communityA = CommunityBuilder.createCommunity(context)
.withName("communityA")
.build();
collectionA = CollectionBuilder.createCollection(context, communityA)
.withName("collectionA")
.build();
collectionB = CollectionBuilder.createCollection(context, communityA)
.withName("collectionB")
.build();
itemA = ItemBuilder.createItem(context, collectionA)
.withTitle("itemA")
.build();
@@ -88,7 +116,7 @@ public class ManageMappedItemsFeatureIT extends AbstractControllerIntegrationTes
.withName("bistreamA")
.build();
}
canManageMappingsFeature = authorizationFeatureService.find(CanManageMappingsFeature.NAME);
context.restoreAuthSystemState();
collectionARest = collectionConverter.convert(collectionA, Projection.DEFAULT);
@@ -188,4 +216,65 @@ public class ManageMappedItemsFeatureIT extends AbstractControllerIntegrationTes
.andExpect(jsonPath("$.page.totalElements", is(0)))
.andExpect(jsonPath("$._embedded").doesNotExist());
}
@Test
@SuppressWarnings("unchecked")
public void canManageMappingsWithUserThatCanManageTwoCollectionsTest() throws Exception {
context.turnOffAuthorisationSystem();
authorizeService.addPolicy(context, collectionA, Constants.ADD, userA);
authorizeService.addPolicy(context, collectionB, Constants.ADD, userA);
context.restoreAuthSystemState();
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
String tokenAdmin = getAuthToken(admin.getEmail(), password);
String tokenAUser = getAuthToken(userA.getEmail(), password);
String tokenEPerson = getAuthToken(eperson.getEmail(), password);
// define authorizations that we know must exists
Authorization admin2ItemA = new Authorization(admin, canManageMappingsFeature, itemRestA);
Authorization userA2ItemA = new Authorization(userA, canManageMappingsFeature, itemRestA);
// define authorization that we know not exists
Authorization eperson2ItemA = new Authorization(eperson, canManageMappingsFeature, itemRestA);
Authorization anonymous2ItemA = new Authorization(null, canManageMappingsFeature, itemRestA);
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(userA2ItemA))));
getClient(tokenEPerson).perform(get("/api/authz/authorizations/" + eperson2ItemA.getID()))
.andExpect(status().isNotFound());
getClient().perform(get("/api/authz/authorizations/" + anonymous2ItemA.getID()))
.andExpect(status().isNotFound());
}
@Test
@SuppressWarnings("unchecked")
public void canManageMappingsOnlyAdminHasAccessTest() throws Exception {
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
String tokenAdmin = getAuthToken(admin.getEmail(), password);
String tokenAUser = getAuthToken(userA.getEmail(), password);
// define authorizations that we know must exists
Authorization admin2ItemA = new Authorization(admin, canManageMappingsFeature, itemRestA);
// define authorization that we know not exists
Authorization userA2ItemA = new Authorization(userA, canManageMappingsFeature, itemRestA);
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
.andExpect(status().isNotFound());
}
}

View File

@@ -0,0 +1,213 @@
/**
* 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 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.authorization.impl.CanManageRelationshipsFeature;
import org.dspace.app.rest.converter.ItemConverter;
import org.dspace.app.rest.matcher.AuthorizationMatcher;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.projection.DefaultProjection;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.EPersonBuilder;
import org.dspace.builder.ItemBuilder;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.core.Constants;
import org.dspace.eperson.EPerson;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Test for the canManageRelationships authorization feature.
*
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
*/
public class CanManageRelationshipsFeatureIT extends AbstractControllerIntegrationTest {
@Autowired
private ItemConverter itemConverter;
@Autowired
private AuthorizeService authorizeService;
@Autowired
private AuthorizationFeatureService authorizationFeatureService;
private Item itemA;
private Item itemB;
private EPerson userA;
private EPerson userB;
private EPerson userColAadmin;
private EPerson userColBadmin;
private EPerson userComAdmin;
private Community communityA;
private Collection collectionA;
private Collection collectionB;
private AuthorizationFeature canManageRelationshipsFeature;
final String feature = "canManageRelationships";
@Before
@Override
public void setUp() throws Exception {
super.setUp();
context.turnOffAuthorisationSystem();
canManageRelationshipsFeature = authorizationFeatureService.find(CanManageRelationshipsFeature.NAME);
userA = EPersonBuilder.createEPerson(context)
.withEmail("userEmail@test.com")
.withPassword(password).build();
userB = EPersonBuilder.createEPerson(context)
.withEmail("userB.email@test.com")
.withPassword(password).build();
userColAadmin = EPersonBuilder.createEPerson(context)
.withEmail("userColAadmin@test.com")
.withPassword(password).build();
userColBadmin = EPersonBuilder.createEPerson(context)
.withEmail("userColBadmin@test.com")
.withPassword(password).build();
userComAdmin = EPersonBuilder.createEPerson(context)
.withEmail("userComAdmin@test.com")
.withPassword(password).build();
communityA = CommunityBuilder.createCommunity(context)
.withName("communityA")
.withAdminGroup(userComAdmin).build();
collectionA = CollectionBuilder.createCollection(context, communityA)
.withName("Collection A")
.withAdminGroup(userColAadmin).build();
collectionB = CollectionBuilder.createCollection(context, communityA)
.withName("Collection B")
.withAdminGroup(userColBadmin).build();
itemA = ItemBuilder.createItem(context, collectionA)
.withTitle("Item A").build();
itemB = ItemBuilder.createItem(context, collectionB)
.withTitle("Item B").build();
context.restoreAuthSystemState();
}
@Test
@SuppressWarnings("unchecked")
public void canManageRelationshipsFeatureTest() throws Exception {
context.turnOffAuthorisationSystem();
// permissions for userA
authorizeService.addPolicy(context, itemA, Constants.WRITE, userA);
// permissions for userB
authorizeService.addPolicy(context, itemB, Constants.WRITE, userB);
context.restoreAuthSystemState();
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
ItemRest itemRestB = itemConverter.convert(itemB, DefaultProjection.DEFAULT);
String tokenAdmin = getAuthToken(admin.getEmail(), password);
String tokenComAdmin = getAuthToken(userComAdmin.getEmail(), password);
String tokenColAadmin = getAuthToken(userColAadmin.getEmail(), password);
String tokenColBadmin = getAuthToken(userColBadmin.getEmail(), password);
String tokenAUser = getAuthToken(userA.getEmail(), password);
String tokenBUser = getAuthToken(userB.getEmail(), password);
String tokenEPerson = getAuthToken(eperson.getEmail(), password);
// define authorizations that we know must exists
Authorization admin2ItemA = new Authorization(admin, canManageRelationshipsFeature, itemRestA);
Authorization admin2ItemB = new Authorization(admin, canManageRelationshipsFeature, itemRestB);
Authorization comAdmin2ItemA = new Authorization(userComAdmin, canManageRelationshipsFeature, itemRestA);
Authorization comAdmin2ItemB = new Authorization(userComAdmin, canManageRelationshipsFeature, itemRestB);
Authorization colAadmin2ItemA = new Authorization(userColAadmin, canManageRelationshipsFeature, itemRestA);
Authorization colBadmin2ItemB = new Authorization(userColBadmin, canManageRelationshipsFeature, itemRestB);
Authorization userA2ItemA = new Authorization(userA, canManageRelationshipsFeature, itemRestA);
Authorization userB2ItemB = new Authorization(userB, canManageRelationshipsFeature, itemRestB);
// define authorization that we know not exists
Authorization userB2ItemA = new Authorization(userB, canManageRelationshipsFeature, itemRestA);
Authorization userA2ItemB = new Authorization(userA, canManageRelationshipsFeature, itemRestB);
Authorization eperson2ItemA = new Authorization(eperson, canManageRelationshipsFeature, itemRestA);
Authorization eperson2ItemB = new Authorization(eperson, canManageRelationshipsFeature, itemRestB);
Authorization anonymous2ItemA = new Authorization(null, canManageRelationshipsFeature, itemRestA);
Authorization anonymous2ItemB = new Authorization(null, canManageRelationshipsFeature, itemRestB);
Authorization colAadmin2ItemB = new Authorization(userColAadmin, canManageRelationshipsFeature, itemRestB);
Authorization colBadmin2ItemA = new Authorization(userColBadmin, canManageRelationshipsFeature, itemRestA);
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemB.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemB))));
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemA))));
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemB.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemB))));
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(colAadmin2ItemA))));
getClient(tokenColBadmin).perform(get("/api/authz/authorizations/" + colBadmin2ItemB.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(colBadmin2ItemB))));
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(userA2ItemA))));
getClient(tokenBUser).perform(get("/api/authz/authorizations/" + userB2ItemB.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(userB2ItemB))));
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemB.getID()))
.andExpect(status().isNotFound());
getClient(tokenColBadmin).perform(get("/api/authz/authorizations/" + colBadmin2ItemA.getID()))
.andExpect(status().isNotFound());
getClient(tokenBUser).perform(get("/api/authz/authorizations/" + userB2ItemA.getID()))
.andExpect(status().isNotFound());
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemB.getID()))
.andExpect(status().isNotFound());
getClient(tokenEPerson).perform(get("/api/authz/authorizations/" + eperson2ItemA.getID()))
.andExpect(status().isNotFound());
getClient(tokenEPerson).perform(get("/api/authz/authorizations/" + eperson2ItemB.getID()))
.andExpect(status().isNotFound());
getClient().perform(get("/api/authz/authorizations/" + anonymous2ItemA.getID()))
.andExpect(status().isNotFound());
getClient().perform(get("/api/authz/authorizations/" + anonymous2ItemB.getID()))
.andExpect(status().isNotFound());
}
}

View File

@@ -0,0 +1,154 @@
/**
* 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 static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.dspace.app.rest.converter.ItemConverter;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.app.rest.utils.Utils;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.EPersonBuilder;
import org.dspace.builder.ItemBuilder;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.eperson.EPerson;
import org.dspace.services.ConfigurationService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Test for the canManageVersions authorization feature.
*
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
*/
public class CanManageVersionsFeatureIT extends AbstractControllerIntegrationTest {
@Autowired
private Utils utils;
@Autowired
private ItemConverter itemConverter;
@Autowired
private ConfigurationService configurationService;
private Item itemA;
private EPerson user;
private ItemRest itemARest;
private Community communityA;
private Collection collectionA;
final String feature = "canManageVersions";
@Override
@Before
public void setUp() throws Exception {
super.setUp();
context.turnOffAuthorisationSystem();
user = EPersonBuilder.createEPerson(context)
.withEmail("userEmail@test.com")
.withPassword(password).build();
communityA = CommunityBuilder.createCommunity(context)
.withName("communityA").build();
collectionA = CollectionBuilder.createCollection(context, communityA)
.withName("collectionA").build();
itemA = ItemBuilder.createItem(context, collectionA)
.withTitle("itemA").build();
context.restoreAuthSystemState();
itemARest = itemConverter.convert(itemA, Projection.DEFAULT);
}
@Test
public void anonymousHasNotAccessTest() throws Exception {
getClient().perform(get("/api/authz/authorizations/search/object")
.param("embed", "feature")
.param("feature", feature)
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.totalElements", is(0)))
.andExpect(jsonPath("$._embedded").doesNotExist());
}
@Test
public void epersonHasNotAccessTest() throws Exception {
String epersonToken = getAuthToken(eperson.getEmail(), password);
getClient(epersonToken).perform(get("/api/authz/authorizations/search/object")
.param("embed", "feature")
.param("feature", feature)
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.totalElements", is(0)))
.andExpect(jsonPath("$._embedded").doesNotExist());
}
@Test
public void adminItemSuccessTest() throws Exception {
String adminToken = getAuthToken(admin.getEmail(), password);
getClient(adminToken).perform(get("/api/authz/authorizations/search/object")
.param("embed", "feature")
.param("feature", feature)
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.totalElements", greaterThan(0)))
.andExpect(jsonPath("$._embedded").exists());
}
@Test
public void submitterItemSuccessTest() throws Exception {
context.turnOffAuthorisationSystem();
configurationService.setProperty("versioning.submitterCanCreateNewVersion", true);
itemA.setSubmitter(user);
context.restoreAuthSystemState();
String userToken = getAuthToken(user.getEmail(), password);
getClient(userToken).perform(get("/api/authz/authorizations/search/object")
.param("embed", "feature")
.param("feature", feature)
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.totalElements", greaterThan(0)))
.andExpect(jsonPath("$._embedded").exists());
}
@Test
public void submitterItemWithPropertySubmitterCanCreateNewVersionIsFalseTest() throws Exception {
context.turnOffAuthorisationSystem();
configurationService.setProperty("versioning.submitterCanCreateNewVersion", false);
itemA.setSubmitter(user);
context.restoreAuthSystemState();
String userToken = getAuthToken(user.getEmail(), password);
getClient(userToken).perform(get("/api/authz/authorizations/search/object")
.param("embed", "feature")
.param("feature", feature)
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.totalElements", is(0)))
.andExpect(jsonPath("$._embedded").doesNotExist());
}
}

View File

@@ -41,9 +41,9 @@ import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Test for the canViewVersions authorization feature
* Test for the canSeeVersions authorization feature
*/
public class ViewVersionsFeatureIT extends AbstractControllerIntegrationTest {
public class CanSeeVersionsFeatureIT extends AbstractControllerIntegrationTest {
@Autowired
private Utils utils;
@@ -68,7 +68,7 @@ public class ViewVersionsFeatureIT extends AbstractControllerIntegrationTest {
private BitstreamRest bitstreamARest;
private Bundle bundleA;
final String feature = "canViewVersions";
final String feature = "canSeeVersions";
@Override
@Before

View File

@@ -99,9 +99,11 @@ public class DiscoverConfigurationConverterTest {
DiscoverySortFieldConfiguration discoverySortFieldConfiguration = new DiscoverySortFieldConfiguration();
discoverySortFieldConfiguration.setMetadataField("title");
discoverySortFieldConfiguration.setType("text");
discoverySortFieldConfiguration.setDefaultSortOrder(DiscoverySortFieldConfiguration.SORT_ORDER.asc);
DiscoverySortFieldConfiguration discoverySortFieldConfiguration1 = new DiscoverySortFieldConfiguration();
discoverySortFieldConfiguration1.setMetadataField("author");
discoverySortFieldConfiguration1.setType("text");
discoverySortFieldConfiguration1.setDefaultSortOrder(DiscoverySortFieldConfiguration.SORT_ORDER.asc);
LinkedList<DiscoverySortFieldConfiguration> mockedList = new LinkedList<>();
mockedList.add(discoverySortFieldConfiguration);
mockedList.add(discoverySortFieldConfiguration1);

View File

@@ -48,4 +48,10 @@ public class SortOptionMatcher {
);
}
public static Matcher<? super Object> sortOptionMatcher(String name, String sortDirection) {
return allOf(
hasJsonPath("$.name", is(name)),
hasJsonPath("$.sortOrder", is(sortDirection))
);
}
}

View File

@@ -86,7 +86,7 @@ public class EmbeddedPageHeaderTest {
Map<String,Object> links = embeddedPageHeader.getLinks();
// "self" should be same as URL
assertEquals(dspaceURL, ((EmbeddedPageHeader.Href) links.get("self")).getHref());
assertEquals(dspaceURL + "?size=10", ((EmbeddedPageHeader.Href) links.get("self")).getHref());
// "first" should not exist, as we are on the first page.
assertFalse(links.containsKey("first"));
// "prev" should not exist, as we are on the first page.

View File

@@ -28,6 +28,7 @@ import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.when;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
@@ -35,6 +36,7 @@ import java.util.List;
import java.util.function.Function;
import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.exception.InvalidSearchRequestException;
import org.dspace.app.rest.parameter.SearchFilter;
import org.dspace.core.Context;
import org.dspace.discovery.DiscoverFacetField;
@@ -111,7 +113,8 @@ public class DiscoverQueryBuilderTest {
any(), any(DiscoverQuery.class)))
.then(invocation -> new FacetYearRange((DiscoverySearchFilterFacet) invocation.getArguments()[2]));
when(searchService.toFilterQuery(any(Context.class), any(String.class), any(String.class), any(String.class)))
when(searchService.toFilterQuery(any(Context.class), any(String.class), any(String.class), any(String.class),
any(DiscoveryConfiguration.class)))
.then(invocation -> new DiscoverFilterQuery((String) invocation.getArguments()[1],
invocation.getArguments()[1] + ":\"" + invocation.getArguments()[3] + "\"",
(String) invocation.getArguments()[3]));
@@ -139,17 +142,23 @@ public class DiscoverQueryBuilderTest {
discoveryConfiguration.setHitHighlightingConfiguration(discoveryHitHighlightingConfiguration);
DiscoverySortConfiguration sortConfiguration = new DiscoverySortConfiguration();
DiscoverySortFieldConfiguration defaultSort = new DiscoverySortFieldConfiguration();
defaultSort.setMetadataField("dc.date.accessioned");
defaultSort.setType(DiscoveryConfigurationParameters.TYPE_DATE);
sortConfiguration.setDefaultSort(defaultSort);
sortConfiguration.setDefaultSortOrder(DiscoverySortConfiguration.SORT_ORDER.desc);
defaultSort.setDefaultSortOrder(DiscoverySortFieldConfiguration.SORT_ORDER.desc);
List<DiscoverySortFieldConfiguration> listSortField = new ArrayList<DiscoverySortFieldConfiguration>();
listSortField.add(defaultSort);
DiscoverySortConfiguration sortConfiguration = new DiscoverySortConfiguration();
DiscoverySortFieldConfiguration titleSort = new DiscoverySortFieldConfiguration();
titleSort.setMetadataField("dc.title");
sortConfiguration.setSortFields(Arrays.asList(titleSort));
titleSort.setDefaultSortOrder(DiscoverySortFieldConfiguration.SORT_ORDER.asc);
listSortField.add(titleSort);
sortConfiguration.setSortFields(listSortField);
discoveryConfiguration.setSearchSortConfiguration(sortConfiguration);
@@ -266,7 +275,7 @@ public class DiscoverQueryBuilderTest {
.buildQuery(context, scope, discoveryConfiguration, query, Arrays.asList(searchFilter), "TEST", page);
}
@Test(expected = DSpaceBadRequestException.class)
@Test(expected = InvalidSearchRequestException.class)
public void testInvalidSortField() throws Exception {
page = PageRequest.of(2, 10, Sort.Direction.ASC, "test");
queryBuilder
@@ -283,7 +292,8 @@ public class DiscoverQueryBuilderTest {
@Test(expected = DSpaceBadRequestException.class)
public void testInvalidSearchFilter2() throws Exception {
when(searchService.toFilterQuery(any(Context.class), any(String.class), any(String.class), any(String.class)))
when(searchService.toFilterQuery(any(Context.class), any(String.class), any(String.class), any(String.class),
any(DiscoveryConfiguration.class)))
.thenThrow(SQLException.class);
queryBuilder

View File

@@ -426,10 +426,9 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
service = (T) applicationContext.getBean(name, type);
} catch (BeansException e) {
// no luck, try the fall back option
log.info(
log.warn(
"Unable to locate bean by name or id={}."
+ " Will try to look up bean by type next."
+ " BeansException: {}", name, e.getMessage());
+ " Will try to look up bean by type next.", name, e);
service = null;
}
} else {
@@ -438,9 +437,8 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
service = (T) applicationContext.getBean(type.getName(), type);
} catch (BeansException e) {
// no luck, try the fall back option
log.info("Unable to locate bean by name or id={}."
+ " Will try to look up bean by type next."
+ " BeansException: {}", type.getName(), e.getMessage());
log.warn("Unable to locate bean by name or id={}."
+ " Will try to look up bean by type next.", type.getName(), e);
service = null;
}
}

View File

@@ -188,6 +188,7 @@ db.schema = public
#plugin.sequence.org.dspace.authenticate.AuthenticationMethod = org.dspace.authenticate.LDAPAuthentication
# Shibboleth authentication/authorization. See authentication-shibboleth.cfg for default configuration.
# Check also the cors settings below
#plugin.sequence.org.dspace.authenticate.AuthenticationMethod = org.dspace.authenticate.ShibAuthentication
# X.509 certificate authentication. See authentication-x509.cfg for default configuration.
@@ -205,6 +206,9 @@ db.schema = public
# Defaults to ${dspace.ui.url} if unspecified (as the UI must have access to the REST API).
# Multiple allowed origin URLs may be comma separated. Wildcard value (*) is NOT SUPPORTED.
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
# When an external authentication system is involved like Shibboleth some browsers (i.e. Safari) include
# in the request the Origin header with the url of the IdP. In such case you need to allow also the IdP to
# avoid trouble for such browsers (i.e. rest.cors.allowed-origins = ${dspace.ui.url}, https://samltest.id )
#rest.cors.allowed-origins = ${dspace.ui.url}
#################################################

View File

@@ -82,7 +82,7 @@
<Logger name='org.dspace.services'
level='ERROR'/>
<Logger name='org.dspace.servicemanager'
level='ERROR'/>
level='WARN'/>
<Logger name='org.dspace.providers'
level='ERROR'/>
<Logger name='org.dspace.utils'

View File

@@ -154,11 +154,9 @@
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
<property name="defaultSortOrder" value="desc"/>
<property name="sortFields">
<list>
<ref bean="sortScore" />
<ref bean="sortTitle" />
<ref bean="sortDateIssued" />
<ref bean="sortDateAccessioned"/>
@@ -295,11 +293,9 @@
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
<property name="defaultSortOrder" value="desc"/>
<property name="sortFields">
<list>
<ref bean="sortScore" />
<ref bean="sortTitle" />
<ref bean="sortDateIssued" />
<ref bean="sortDateAccessioned"/>
@@ -441,11 +437,9 @@
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
<property name="defaultSortOrder" value="desc"/>
<property name="sortFields">
<list>
<ref bean="sortScore" />
<ref bean="sortTitle" />
<ref bean="sortDateIssued" />
<ref bean="sortDateAccessioned"/>
@@ -581,11 +575,9 @@
<!--The sort filters for the discovery search (same as defaultConfiguration above)-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
<property name="defaultSortOrder" value="desc"/>
<property name="sortFields">
<list>
<ref bean="sortScore" />
<ref bean="sortTitle" />
<ref bean="sortDateIssued" />
<ref bean="sortDateAccessioned" />
@@ -687,11 +679,9 @@
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
<property name="defaultSortOrder" value="desc"/>
<property name="sortFields">
<list>
<ref bean="sortScore" />
<ref bean="sortTitle" />
<ref bean="sortDateIssued" />
</list>
@@ -764,11 +754,9 @@
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
<property name="defaultSortOrder" value="desc"/>
<property name="sortFields">
<list>
<ref bean="sortScore" />
<ref bean="sortTitle" />
<ref bean="sortDateIssued" />
</list>
@@ -841,11 +829,9 @@
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
<property name="defaultSortOrder" value="desc"/>
<property name="sortFields">
<list>
<ref bean="sortScore" />
<ref bean="sortTitle" />
<ref bean="sortDateIssued" />
</list>
@@ -927,11 +913,9 @@
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
<property name="defaultSortOrder" value="desc"/>
<property name="sortFields">
<list>
<ref bean="sortScore" />
<ref bean="sortTitle" />
<ref bean="sortDateIssued" />
<ref bean="sortDateAccessioned"/>
@@ -989,11 +973,9 @@
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
<property name="defaultSortOrder" value="desc"/>
<property name="sortFields">
<list>
<ref bean="sortScore" />
<ref bean="sortFamilyName"/>
<ref bean="sortGivenName"/>
<ref bean="sortBirthDate"/>
@@ -1047,11 +1029,9 @@
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
<property name="defaultSortOrder" value="desc"/>
<property name="sortFields">
<list>
<ref bean="sortScore" />
<ref bean="sortTitle"/>
</list>
</property>
@@ -1107,11 +1087,9 @@
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
<property name="defaultSortOrder" value="desc"/>
<property name="sortFields">
<list>
<ref bean="sortScore" />
<ref bean="sortOrganizationLegalName"/>
<ref bean="sortOrganizationAddressCountry"/>
<ref bean="sortOrganizationAddressLocality"/>
@@ -1169,11 +1147,9 @@
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
<property name="defaultSortOrder" value="desc"/>
<property name="sortFields">
<list>
<ref bean="sortScore" />
<ref bean="sortPublicationIssueNumber"/>
<ref bean="sortTitle"/>
<ref bean="sortCreativeWorkDatePublished"/>
@@ -1229,11 +1205,9 @@
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
<property name="defaultSortOrder" value="desc"/>
<property name="sortFields">
<list>
<ref bean="sortScore" />
<ref bean="sortPublicationVolumeNumber"/>
<ref bean="sortTitle"/>
<ref bean="sortCreativeWorkDatePublished"/>
@@ -1290,11 +1264,9 @@
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
<property name="defaultSortOrder" value="desc"/>
<property name="sortFields">
<list>
<ref bean="sortScore" />
<ref bean="sortTitle"/>
<ref bean="sortCreativeWorkDatePublished"/>
<ref bean="sortDateAccessioned"/>
@@ -1360,9 +1332,6 @@
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<property name="defaultSort" ref="sortEntityType"/>
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
<property name="defaultSortOrder" value="desc"/>
<property name="sortFields">
<list>
<ref bean="sortEntityType"/>
@@ -1374,7 +1343,6 @@
<ref bean="sortFamilyName"/>
<ref bean="sortGivenName"/>
<ref bean="sortBirthDate"/>
<ref bean="sortDateAccessioned"/>
</list>
</property>
</bean>
@@ -1422,11 +1390,9 @@
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
<property name="defaultSortOrder" value="desc"/>
<property name="sortFields">
<list>
<ref bean="sortScore" />
<ref bean="sortOrganizationLegalName"/>
<ref bean="sortOrganizationAddressCountry"/>
<ref bean="sortOrganizationAddressLocality"/>
@@ -2180,59 +2146,77 @@
</bean>
<!--Sort properties-->
<bean id="sortScore" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
<property name="defaultSortOrder" value="desc"/>
</bean>
<bean id="sortTitle" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
<property name="metadataField" value="dc.title"/>
<property name="defaultSortOrder" value="asc"/>
</bean>
<bean id="sortDateIssued" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
<property name="metadataField" value="dc.date.issued"/>
<property name="type" value="date"/>
<property name="defaultSortOrder" value="desc"/>
</bean>
<bean id="sortDateAccessioned" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
<property name="metadataField" value="dc.date.accessioned"/>
<property name="type" value="date"/>
<property name="defaultSortOrder" value="desc"/>
</bean>
<bean id="sortFamilyName" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
<property name="metadataField" value="person.familyName"/>
<property name="defaultSortOrder" value="asc"/>
</bean>
<bean id="sortGivenName" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
<property name="metadataField" value="person.givenName"/>
<property name="defaultSortOrder" value="asc"/>
</bean>
<bean id="sortBirthDate" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
<property name="metadataField" value="person.birthDate"/>
<property name="type" value="date"/>
<property name="defaultSortOrder" value="desc"/>
</bean>
<bean id="sortOrganizationLegalName" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
<property name="metadataField" value="organization.legalName"/>
<property name="defaultSortOrder" value="asc"/>
</bean>
<bean id="sortOrganizationAddressCountry"
class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
<property name="metadataField" value="organisation.address.addressCountry"/>
<property name="defaultSortOrder" value="asc"/>
</bean>
<bean id="sortOrganizationAddressLocality"
class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
<property name="metadataField" value="organisation.address.addressLocality"/>
<property name="defaultSortOrder" value="asc"/>
</bean>
<bean id="sortOrganizationFoundingDate" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
<property name="metadataField" value="organisation.foundingDate"/>
<property name="type" value="date"/>
<property name="defaultSortOrder" value="desc"/>
</bean>
<bean id="sortPublicationIssueNumber" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
<property name="metadataField" value="publicationissue.issueNumber"/>
<property name="defaultSortOrder" value="desc"/>
</bean>
<bean id="sortCreativeWorkDatePublished" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
<property name="metadataField" value="creativework.datePublished"/>
<property name="defaultSortOrder" value="desc"/>
</bean>
<bean id="sortPublicationVolumeNumber" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
<property name="metadataField" value="publicationvolume.volumeNumber"/>
<property name="defaultSortOrder" value="desc"/>
</bean>
<bean id="sortEntityType" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
<property name="metadataField" value="dspace.entity.type"/>
<property name="defaultSortOrder" value="desc"/>
</bean>
</beans>

View File

@@ -35,7 +35,7 @@
<jaxb-api.version>2.3.1</jaxb-api.version>
<jaxb-runtime.version>2.3.1</jaxb-runtime.version>
<!-- NOTE: Jetty needed for Solr, Handle Server & tests -->
<jetty.version>9.4.35.v20201120</jetty.version>
<jetty.version>9.4.38.v20210224</jetty.version>
<log4j.version>2.13.3</log4j.version>
<pdfbox-version>2.0.15</pdfbox-version>
<poi-version>3.17</poi-version>