Merge branch 'main' into CST-7756-SubscriptionFeature

# Conflicts:
#	dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java
This commit is contained in:
corrado lombardi
2023-02-02 17:52:50 +01:00
16 changed files with 1114 additions and 36 deletions

View File

@@ -51,9 +51,15 @@ import org.dspace.content.virtual.VirtualMetadataPopulator;
import org.dspace.core.Constants; import org.dspace.core.Constants;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.core.LogHelper; import org.dspace.core.LogHelper;
import org.dspace.discovery.DiscoverQuery;
import org.dspace.discovery.DiscoverResult;
import org.dspace.discovery.SearchService;
import org.dspace.discovery.SearchServiceException;
import org.dspace.discovery.indexobject.IndexableItem;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group; import org.dspace.eperson.Group;
import org.dspace.eperson.service.SubscribeService; import org.dspace.eperson.service.SubscribeService;
import org.dspace.eperson.service.GroupService;
import org.dspace.event.Event; import org.dspace.event.Event;
import org.dspace.harvest.HarvestedItem; import org.dspace.harvest.HarvestedItem;
import org.dspace.harvest.service.HarvestedItemService; import org.dspace.harvest.service.HarvestedItemService;
@@ -94,6 +100,8 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
@Autowired(required = true) @Autowired(required = true)
protected CommunityService communityService; protected CommunityService communityService;
@Autowired(required = true) @Autowired(required = true)
protected GroupService groupService;
@Autowired(required = true)
protected AuthorizeService authorizeService; protected AuthorizeService authorizeService;
@Autowired(required = true) @Autowired(required = true)
protected BundleService bundleService; protected BundleService bundleService;
@@ -106,6 +114,8 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
@Autowired(required = true) @Autowired(required = true)
protected InstallItemService installItemService; protected InstallItemService installItemService;
@Autowired(required = true) @Autowired(required = true)
protected SearchService searchService;
@Autowired(required = true)
protected ResourcePolicyService resourcePolicyService; protected ResourcePolicyService resourcePolicyService;
@Autowired(required = true) @Autowired(required = true)
protected CollectionService collectionService; protected CollectionService collectionService;
@@ -1070,6 +1080,53 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
return collectionService.canEditBoolean(context, item.getOwningCollection(), false); return collectionService.canEditBoolean(context, item.getOwningCollection(), false);
} }
/**
* Finds all Indexed Items where the current user has edit rights. If the user is an Admin,
* this is all Indexed Items. Otherwise, it includes those Items where
* an indexed "edit" policy lists either the eperson or one of the eperson's groups
*
* @param context DSpace context
* @param discoverQuery
* @return discovery search result objects
* @throws SQLException if something goes wrong
* @throws SearchServiceException if search error
*/
private DiscoverResult retrieveItemsWithEdit(Context context, DiscoverQuery discoverQuery)
throws SQLException, SearchServiceException {
EPerson currentUser = context.getCurrentUser();
if (!authorizeService.isAdmin(context)) {
String userId = currentUser != null ? "e" + currentUser.getID().toString() : "e";
Stream<String> groupIds = groupService.allMemberGroupsSet(context, currentUser).stream()
.map(group -> "g" + group.getID());
String query = Stream.concat(Stream.of(userId), groupIds)
.collect(Collectors.joining(" OR ", "edit:(", ")"));
discoverQuery.addFilterQueries(query);
}
return searchService.search(context, discoverQuery);
}
@Override
public List<Item> findItemsWithEdit(Context context, int offset, int limit)
throws SQLException, SearchServiceException {
DiscoverQuery discoverQuery = new DiscoverQuery();
discoverQuery.setDSpaceObjectFilter(IndexableItem.TYPE);
discoverQuery.setStart(offset);
discoverQuery.setMaxResults(limit);
DiscoverResult resp = retrieveItemsWithEdit(context, discoverQuery);
return resp.getIndexableObjects().stream()
.map(solrItems -> ((IndexableItem) solrItems).getIndexedObject())
.collect(Collectors.toList());
}
@Override
public int countItemsWithEdit(Context context) throws SQLException, SearchServiceException {
DiscoverQuery discoverQuery = new DiscoverQuery();
discoverQuery.setMaxResults(0);
discoverQuery.setDSpaceObjectFilter(IndexableItem.TYPE);
DiscoverResult resp = retrieveItemsWithEdit(context, discoverQuery);
return (int) resp.getTotalSearchResults();
}
/** /**
* Check if the item is an inprogress submission * Check if the item is an inprogress submission
* *

View File

@@ -28,6 +28,7 @@ import org.dspace.content.MetadataValue;
import org.dspace.content.Thumbnail; import org.dspace.content.Thumbnail;
import org.dspace.content.WorkspaceItem; import org.dspace.content.WorkspaceItem;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.discovery.SearchServiceException;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group; import org.dspace.eperson.Group;
@@ -768,6 +769,27 @@ public interface ItemService
*/ */
int countWithdrawnItems(Context context) throws SQLException; int countWithdrawnItems(Context context) throws SQLException;
/**
* finds all items for which the current user has editing rights
* @param context DSpace context object
* @param offset page offset
* @param limit page size limit
* @return list of items for which the current user has editing rights
* @throws SQLException
* @throws SearchServiceException
*/
public List<Item> findItemsWithEdit(Context context, int offset, int limit)
throws SQLException, SearchServiceException;
/**
* counts all items for which the current user has editing rights
* @param context DSpace context object
* @return list of items for which the current user has editing rights
* @throws SQLException
* @throws SearchServiceException
*/
public int countItemsWithEdit(Context context) throws SQLException, SearchServiceException;
/** /**
* Check if the supplied item is an inprogress submission * Check if the supplied item is an inprogress submission
* *

View File

@@ -0,0 +1,119 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.discovery;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.DSpaceObject;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.core.Context;
/**
* Util methods used by indexing.
*
* @author Koen Pauwels (koen.pauwels at atmire dot com)
*/
public class IndexingUtils {
private IndexingUtils() {
}
/**
* Retrieve all ancestor communities of a given community, with the first one being the given community and the
* last one being the root.
* <p>
*
* @param context DSpace context object
* @param community Community for which we search the ancestors
* @return A list of ancestor communities.
* @throws SQLException if database error
*/
static List<Community> getAncestorCommunities(Context context, Community community) throws SQLException {
ArrayList<Community> communities = new ArrayList<>();
while (community != null) {
communities.add(community);
community = (Community) ContentServiceFactory.getInstance().getDSpaceObjectService(community)
.getParentObject(context, community);
}
return communities;
}
/**
* Retrieve the ids of all groups that have ADMIN rights to the given community, either directly
* (through direct resource policy) or indirectly (through a policy on an ancestor community).
*
* @param context DSpace context object
* @param community Community for which we search the admin group IDs
* @return A list of admin group IDs
* @throws SQLException if database error
*/
static List<UUID> findTransitiveAdminGroupIds(Context context, Community community) throws SQLException {
return getAncestorCommunities(context, community).stream()
.filter(parent -> parent.getAdministrators() != null)
.map(parent -> parent.getAdministrators().getID())
.collect(Collectors.toList());
}
/**
* Retrieve the ids of all groups that have ADMIN rights to the given collection, either directly
* (through direct resource policy) or indirectly (through a policy on its community, or one of
* its ancestor communities).
*
* @param context DSpace context object
* @param collection Collection for which we search the admin group IDs
* @return A list of admin group IDs
* @throws SQLException if database error
*/
static List<UUID> findTransitiveAdminGroupIds(Context context, Collection collection) throws SQLException {
List<UUID> ids = new ArrayList<>();
if (collection.getAdministrators() != null) {
ids.add(collection.getAdministrators().getID());
}
for (Community community : collection.getCommunities()) {
for (UUID id : findTransitiveAdminGroupIds(context, community)) {
ids.add(id);
}
}
return ids;
}
/**
* Retrieve group and eperson IDs for all groups and eperson who have _any_ of the given authorizations
* on the given DSpaceObject. The resulting IDs are prefixed with "e" in the case of an eperson ID, and "g" in the
* case of a group ID.
*
* @param authService The authentication service
* @param context DSpace context object
* @param obj DSpaceObject for which we search the admin group IDs
* @return A stream of admin group IDs as Strings, prefixed with either "e" or "g", depending on whether it is a
* group or eperson ID.
* @throws SQLException if database error
*/
static List<String> findDirectlyAuthorizedGroupAndEPersonPrefixedIds(
AuthorizeService authService, Context context, DSpaceObject obj, int[] authorizations)
throws SQLException {
ArrayList<String> prefixedIds = new ArrayList<>();
for (int auth : authorizations) {
for (ResourcePolicy policy : authService.getPoliciesActionFilter(context, obj, auth)) {
String prefixedId = policy.getGroup() == null
? "e" + policy.getEPerson().getID()
: "g" + policy.getGroup().getID();
prefixedIds.add(prefixedId);
context.uncacheEntity(policy);
}
}
return prefixedIds;
}
}

View File

@@ -7,16 +7,17 @@
*/ */
package org.dspace.discovery; package org.dspace.discovery;
import static org.dspace.discovery.IndexingUtils.findDirectlyAuthorizedGroupAndEPersonPrefixedIds;
import static org.dspace.discovery.IndexingUtils.findTransitiveAdminGroupIds;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.UUID;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.SolrInputDocument;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService; import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Collection; import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.core.Constants; import org.dspace.core.Constants;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.core.LogHelper; import org.dspace.core.LogHelper;
@@ -42,29 +43,21 @@ public class SolrServiceIndexCollectionSubmittersPlugin implements SolrServiceIn
Collection col = ((IndexableCollection) idxObj).getIndexedObject(); Collection col = ((IndexableCollection) idxObj).getIndexedObject();
if (col != null) { if (col != null) {
try { try {
String fieldValue = null; // Index groups with ADMIN rights on the Collection, on
Community parent = (Community) ContentServiceFactory.getInstance().getDSpaceObjectService(col) // Communities containing those Collections, and recursively on any Community containing such a
.getParentObject(context, col); // Community.
while (parent != null) { // TODO: Strictly speaking we should also check for epersons who received admin rights directly,
if (parent.getAdministrators() != null) { // without being part of the admin group. Finding them may be a lot slower though.
fieldValue = "g" + parent.getAdministrators().getID(); for (UUID unprefixedId : findTransitiveAdminGroupIds(context, col)) {
document.addField("submit", fieldValue); document.addField("submit", "g" + unprefixedId);
}
parent = (Community) ContentServiceFactory.getInstance().getDSpaceObjectService(parent)
.getParentObject(context, parent);
} }
List<ResourcePolicy> policies = authorizeService.getPoliciesActionFilter(context,col,Constants.ADD);
policies.addAll(authorizeService.getPoliciesActionFilter(context, col, Constants.ADMIN));
for (ResourcePolicy resourcePolicy : policies) { // Index groups and epersons with ADD or ADMIN rights on the Collection.
if (resourcePolicy.getGroup() != null) { List<String> prefixedIds = findDirectlyAuthorizedGroupAndEPersonPrefixedIds(
fieldValue = "g" + resourcePolicy.getGroup().getID(); authorizeService, context, col, new int[] {Constants.ADD, Constants.ADMIN}
} else { );
fieldValue = "e" + resourcePolicy.getEPerson().getID(); for (String prefixedId : prefixedIds) {
document.addField("submit", prefixedId);
}
document.addField("submit", fieldValue);
context.uncacheEntity(resourcePolicy);
} }
} catch (SQLException e) { } catch (SQLException e) {
log.error(LogHelper.getHeader(context, "Error while indexing resource policies", log.error(LogHelper.getHeader(context, "Error while indexing resource policies",
@@ -73,5 +66,4 @@ public class SolrServiceIndexCollectionSubmittersPlugin implements SolrServiceIn
} }
} }
} }
}
}

View File

@@ -0,0 +1,71 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.discovery;
import static org.dspace.discovery.IndexingUtils.findDirectlyAuthorizedGroupAndEPersonPrefixedIds;
import static org.dspace.discovery.IndexingUtils.findTransitiveAdminGroupIds;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
import org.apache.logging.log4j.Logger;
import org.apache.solr.common.SolrInputDocument;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Collection;
import org.dspace.content.Item;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.LogHelper;
import org.dspace.discovery.indexobject.IndexableItem;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Indexes policies that yield write access to items.
*
* @author Koen Pauwels at atmire.com
*/
public class SolrServiceIndexItemEditorsPlugin implements SolrServiceIndexPlugin {
private static final Logger log = org.apache.logging.log4j.LogManager
.getLogger(SolrServiceIndexItemEditorsPlugin.class);
@Autowired(required = true)
protected AuthorizeService authorizeService;
@Override
public void additionalIndex(Context context, IndexableObject idxObj, SolrInputDocument document) {
if (idxObj instanceof IndexableItem) {
Item item = ((IndexableItem) idxObj).getIndexedObject();
if (item != null) {
try {
// Index groups with ADMIN rights on Collections containing the Item, on
// Communities containing those Collections, and recursively on any Community containing ssuch a
// Community.
// TODO: Strictly speaking we should also check for epersons who received admin rights directly,
// without being part of the admin group. Finding them may be a lot slower though.
for (Collection collection : item.getCollections()) {
for (UUID unprefixedId : findTransitiveAdminGroupIds(context, collection)) {
document.addField("edit", "g" + unprefixedId);
}
}
// Index groups and epersons with WRITE or direct ADMIN rights on the Item.
List<String> prefixedIds = findDirectlyAuthorizedGroupAndEPersonPrefixedIds(
authorizeService, context, item, new int[] {Constants.WRITE, Constants.ADMIN}
);
for (String prefixedId : prefixedIds) {
document.addField("edit", prefixedId);
}
} catch (SQLException e) {
log.error(LogHelper.getHeader(context, "Error while indexing resource policies",
"Item: (id " + item.getID() + " name " + item.getName() + ")" ));
}
}
}
}
}

View File

@@ -20,12 +20,15 @@ import java.util.stream.Collectors;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.AbstractIntegrationTestWithDatabase;
import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder; import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.EntityTypeBuilder; import org.dspace.builder.EntityTypeBuilder;
import org.dspace.builder.GroupBuilder;
import org.dspace.builder.ItemBuilder; import org.dspace.builder.ItemBuilder;
import org.dspace.builder.RelationshipBuilder; import org.dspace.builder.RelationshipBuilder;
import org.dspace.builder.RelationshipTypeBuilder; import org.dspace.builder.RelationshipTypeBuilder;
import org.dspace.builder.ResourcePolicyBuilder;
import org.dspace.content.Collection; import org.dspace.content.Collection;
import org.dspace.content.Community; import org.dspace.content.Community;
import org.dspace.content.EntityType; import org.dspace.content.EntityType;
@@ -35,6 +38,8 @@ import org.dspace.content.Relationship;
import org.dspace.content.RelationshipType; import org.dspace.content.RelationshipType;
import org.dspace.content.WorkspaceItem; import org.dspace.content.WorkspaceItem;
import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.core.Constants;
import org.dspace.eperson.Group;
import org.dspace.versioning.Version; import org.dspace.versioning.Version;
import org.dspace.versioning.factory.VersionServiceFactory; import org.dspace.versioning.factory.VersionServiceFactory;
import org.dspace.versioning.service.VersioningService; import org.dspace.versioning.service.VersioningService;
@@ -473,6 +478,82 @@ public class ItemServiceTest extends AbstractIntegrationTestWithDatabase {
context.restoreAuthSystemState(); context.restoreAuthSystemState();
} }
@Test
public void testFindItemsWithEditNoRights() throws Exception {
context.setCurrentUser(eperson);
List<Item> result = itemService.findItemsWithEdit(context, 0, 10);
int count = itemService.countItemsWithEdit(context);
assertThat(result.size(), equalTo(0));
assertThat(count, equalTo(0));
}
@Test
public void testFindAndCountItemsWithEditEPerson() throws Exception {
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withUser(eperson)
.withDspaceObject(item)
.withAction(Constants.WRITE)
.build();
context.setCurrentUser(eperson);
List<Item> result = itemService.findItemsWithEdit(context, 0, 10);
int count = itemService.countItemsWithEdit(context);
assertThat(result.size(), equalTo(1));
assertThat(count, equalTo(1));
}
@Test
public void testFindAndCountItemsWithAdminEPerson() throws Exception {
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withUser(eperson)
.withDspaceObject(item)
.withAction(Constants.ADMIN)
.build();
context.setCurrentUser(eperson);
List<Item> result = itemService.findItemsWithEdit(context, 0, 10);
int count = itemService.countItemsWithEdit(context);
assertThat(result.size(), equalTo(1));
assertThat(count, equalTo(1));
}
@Test
public void testFindAndCountItemsWithEditGroup() throws Exception {
context.turnOffAuthorisationSystem();
Group group = GroupBuilder.createGroup(context)
.addMember(eperson)
.build();
context.restoreAuthSystemState();
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withGroup(group)
.withDspaceObject(item)
.withAction(Constants.WRITE)
.build();
context.setCurrentUser(eperson);
List<Item> result = itemService.findItemsWithEdit(context, 0, 10);
int count = itemService.countItemsWithEdit(context);
assertThat(result.size(), equalTo(1));
assertThat(count, equalTo(1));
}
@Test
public void testFindAndCountItemsWithAdminGroup() throws Exception {
context.turnOffAuthorisationSystem();
Group group = GroupBuilder.createGroup(context)
.addMember(eperson)
.build();
context.restoreAuthSystemState();
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withGroup(group)
.withDspaceObject(item)
.withAction(Constants.ADMIN)
.build();
context.setCurrentUser(eperson);
List<Item> result = itemService.findItemsWithEdit(context, 0, 10);
int count = itemService.countItemsWithEdit(context);
assertThat(result.size(), equalTo(1));
assertThat(count, equalTo(1));
}
private void assertMetadataValue(String authorQualifier, String contributorElement, String dcSchema, String value, private void assertMetadataValue(String authorQualifier, String contributorElement, String dcSchema, String value,
String authority, int place, MetadataValue metadataValue) { String authority, int place, MetadataValue metadataValue) {
assertThat(metadataValue.getValue(), equalTo(value)); assertThat(metadataValue.getValue(), equalTo(value));

View File

@@ -13,6 +13,7 @@ import org.dspace.app.rest.model.BaseObjectRest;
import org.dspace.app.rest.model.RestAddressableModel; import org.dspace.app.rest.model.RestAddressableModel;
import org.dspace.app.rest.model.SiteRest; import org.dspace.app.rest.model.SiteRest;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.discovery.SearchServiceException;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
/** /**
@@ -33,7 +34,7 @@ public interface AuthorizationFeature {
* wide feature * wide feature
* @return true if the user associated with the context has access to the feature for the specified object * @return true if the user associated with the context has access to the feature for the specified object
*/ */
boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException; boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException, SearchServiceException;
/** /**
* Return the name of the feature * Return the name of the feature
@@ -69,4 +70,4 @@ public interface AuthorizationFeature {
* @return the supported object type, required to be not null * @return the supported object type, required to be not null
*/ */
String[] getSupportedTypes(); String[] getSupportedTypes();
} }

View File

@@ -13,6 +13,7 @@ import java.util.List;
import org.dspace.app.rest.model.BaseObjectRest; import org.dspace.app.rest.model.BaseObjectRest;
import org.dspace.app.rest.model.SiteRest; import org.dspace.app.rest.model.SiteRest;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.discovery.SearchServiceException;
/** /**
* This service provides access to the Authorization Features and check if the feature is allowed or not in a specific * This service provides access to the Authorization Features and check if the feature is allowed or not in a specific
@@ -34,7 +35,8 @@ public interface AuthorizationFeatureService {
* feature pass the {@link SiteRest} object * feature pass the {@link SiteRest} object
* @return true if the user associated with the context has access to the feature * @return true if the user associated with the context has access to the feature
*/ */
boolean isAuthorized(Context context, AuthorizationFeature feature, BaseObjectRest object) throws SQLException; boolean isAuthorized(Context context, AuthorizationFeature feature, BaseObjectRest object)
throws SQLException, SearchServiceException;
/** /**
* Get all the authorization features defined in the system * Get all the authorization features defined in the system
@@ -60,4 +62,4 @@ public interface AuthorizationFeatureService {
* @return * @return
*/ */
List<AuthorizationFeature> findByResourceType(String categoryDotModel); List<AuthorizationFeature> findByResourceType(String categoryDotModel);
} }

View File

@@ -18,6 +18,7 @@ import org.dspace.app.rest.authorization.AuthorizationFeatureService;
import org.dspace.app.rest.model.BaseObjectRest; import org.dspace.app.rest.model.BaseObjectRest;
import org.dspace.app.rest.utils.Utils; import org.dspace.app.rest.utils.Utils;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.discovery.SearchServiceException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -38,7 +39,7 @@ public class AuthorizationFeatureServiceImpl implements AuthorizationFeatureServ
@Override @Override
public boolean isAuthorized(Context context, AuthorizationFeature feature, BaseObjectRest object) public boolean isAuthorized(Context context, AuthorizationFeature feature, BaseObjectRest object)
throws SQLException { throws SQLException, SearchServiceException {
if (object == null) { if (object == null) {
// the authorization interface require that the object is not null // the authorization interface require that the object is not null
return false; return false;

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.model.SiteRest;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Item;
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;
@Component
@AuthorizationFeatureDocumentation(name = EditItemFeature.NAME,
description = "It can be used to verify if a user has rights to edit any item.")
public class EditItemFeature implements AuthorizationFeature {
public static final String NAME = "canEditItem";
@Autowired
AuthorizeService authService;
@Autowired
ItemService itemService;
@Autowired
Utils utils;
@Override
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException, SearchServiceException {
if (object instanceof SiteRest) {
return itemService.countItemsWithEdit(context) > 0;
} else if (object instanceof ItemRest) {
Item item = (Item) utils.getDSpaceAPIObjectFromRest(context, object);
return authService.authorizeActionBoolean(context, item, Constants.WRITE);
}
return false;
}
@Override
public String[] getSupportedTypes() {
return new String[] {
ItemRest.CATEGORY + "." + ItemRest.NAME,
SiteRest.CATEGORY + "." + SiteRest.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.CollectionRest;
import org.dspace.app.rest.model.SiteRest;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Collection;
import org.dspace.content.service.CollectionService;
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;
@Component
@AuthorizationFeatureDocumentation(name = SubmitFeature.NAME,
description = "It can be used to verify if a user has rights to submit anything.")
public class SubmitFeature implements AuthorizationFeature {
public static final String NAME = "canSubmit";
@Autowired
AuthorizeService authService;
@Autowired
CollectionService collectionService;
@Autowired
Utils utils;
@Override
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException, SearchServiceException {
if (object instanceof SiteRest) {
// Check whether the user has permission to add to any collection
return collectionService.countCollectionsWithSubmit("", context, null) > 0;
} else if (object instanceof CollectionRest) {
// Check whether the user has permission to add to the given collection
Collection collection = (Collection) utils.getDSpaceAPIObjectFromRest(context, object);
return authService.authorizeActionBoolean(context, collection, Constants.ADD);
}
return false;
}
@Override
public String[] getSupportedTypes() {
return new String[] {
CollectionRest.CATEGORY + "." + CollectionRest.NAME,
SiteRest.CATEGORY + "." + SiteRest.NAME
};
}
}

View File

@@ -31,6 +31,7 @@ import org.dspace.app.rest.model.BaseObjectRest;
import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.service.AuthorizeService; import org.dspace.authorize.service.AuthorizeService;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.discovery.SearchServiceException;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.service.EPersonService; import org.dspace.eperson.service.EPersonService;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -124,7 +125,7 @@ public class AuthorizationRestRepository extends DSpaceRestRepository<Authorizat
// restore the real current user // restore the real current user
context.restoreContextUser(); context.restoreContextUser();
} }
} catch (SQLException e) { } catch (SQLException | SearchServiceException e) {
throw new RuntimeException(e.getMessage(), e); throw new RuntimeException(e.getMessage(), e);
} }
@@ -151,7 +152,7 @@ public class AuthorizationRestRepository extends DSpaceRestRepository<Authorizat
@SearchRestMethod(name = "object") @SearchRestMethod(name = "object")
public Page<AuthorizationRest> findByObject(@Parameter(value = "uri", required = true) String uri, public Page<AuthorizationRest> findByObject(@Parameter(value = "uri", required = true) String uri,
@Parameter(value = "eperson") UUID epersonUuid, @Parameter(value = "feature") String featureName, @Parameter(value = "eperson") UUID epersonUuid, @Parameter(value = "feature") String featureName,
Pageable pageable) throws AuthorizeException, SQLException { Pageable pageable) throws AuthorizeException, SQLException, SearchServiceException {
Context context = obtainContext(); Context context = obtainContext();
@@ -234,7 +235,7 @@ public class AuthorizationRestRepository extends DSpaceRestRepository<Authorizat
Context context, Context context,
EPerson user, EPerson user,
String uri, String uri,
String featureName) throws SQLException { String featureName) throws SQLException, SearchServiceException {
BaseObjectRest restObject = utils.getBaseObjectRestFromUri(context, uri); BaseObjectRest restObject = utils.getBaseObjectRestFromUri(context, uri);
return authorizationsForObject(context, user, featureName, restObject); return authorizationsForObject(context, user, featureName, restObject);
@@ -244,7 +245,7 @@ public class AuthorizationRestRepository extends DSpaceRestRepository<Authorizat
Context context, Context context,
EPerson user, String featureName, EPerson user, String featureName,
BaseObjectRest obj) BaseObjectRest obj)
throws SQLException { throws SQLException, SearchServiceException {
if (obj == null) { if (obj == null) {
return new ArrayList<>(); return new ArrayList<>();
@@ -269,7 +270,7 @@ public class AuthorizationRestRepository extends DSpaceRestRepository<Authorizat
private List<Authorization> findByObjectAndFeature( private List<Authorization> findByObjectAndFeature(
Context context, EPerson user, BaseObjectRest obj, String featureName Context context, EPerson user, BaseObjectRest obj, String featureName
) throws SQLException { ) throws SQLException, SearchServiceException {
AuthorizationFeature feature = authorizationFeatureService.find(featureName); AuthorizationFeature feature = authorizationFeatureService.find(featureName);

View File

@@ -0,0 +1,279 @@
/**
* 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 java.util.concurrent.Callable;
import org.dspace.app.rest.authorization.impl.EditItemFeature;
import org.dspace.app.rest.converter.ItemConverter;
import org.dspace.app.rest.converter.SiteConverter;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.model.SiteRest;
import org.dspace.app.rest.projection.DefaultProjection;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.GroupBuilder;
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.content.Site;
import org.dspace.content.service.SiteService;
import org.dspace.core.Constants;
import org.dspace.eperson.Group;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.ResultActions;
public class EditItemFeatureIT extends AbstractControllerIntegrationTest {
@Autowired
private AuthorizationFeatureService authorizationFeatureService;
@Autowired
private SiteService siteService;
@Autowired
private ItemConverter itemConverter;
@Autowired
private SiteConverter siteConverter;
@Autowired
private Utils utils;
private Group group;
private String siteUri;
private String epersonToken;
private AuthorizationFeature editItemFeature;
private Community communityA;
private Collection collectionA1;
private Collection collectionA2;
private Item itemA1X;
private Item itemA2X;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
withSuppressedAuthorization(() -> {
communityA = CommunityBuilder.createCommunity(context).withName("Community A").build();
collectionA1 = CollectionBuilder.createCollection(context, communityA).withName("Collection A1").build();
collectionA2 = CollectionBuilder.createCollection(context, communityA).withName("Collection A2").build();
itemA1X = ItemBuilder.createItem(context, collectionA1).withTitle("Item A1X").build();
itemA2X = ItemBuilder.createItem(context, collectionA2).withTitle("Item A2X").build();
group = GroupBuilder.createGroup(context)
.withName("group")
.addMember(eperson)
.build();
return null;
});
editItemFeature = authorizationFeatureService.find(EditItemFeature.NAME);
Site site = siteService.findSite(context);
SiteRest siteRest = siteConverter.convert(site, DefaultProjection.DEFAULT);
siteUri = utils.linkToSingleResource(siteRest, "self").getHref();
}
@Test
public void testNoRights() throws Exception {
expectZeroResults(requestSitewideEditItemFeature());
}
@Test
public void testDirectEPersonWritePolicy() throws Exception {
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withUser(eperson)
.withDspaceObject(itemA1X)
.withAction(Constants.WRITE)
.build();
expectSomeResults(requestSitewideEditItemFeature());
expectSomeResults(requestEditItemFeature(itemA1X));
expectZeroResults(requestEditItemFeature(itemA2X));
}
@Test
public void testDirectGroupWritePolicy() throws Exception {
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withGroup(group)
.withDspaceObject(itemA1X)
.withAction(Constants.WRITE)
.build();
expectSomeResults(requestSitewideEditItemFeature());
expectSomeResults(requestEditItemFeature(itemA1X));
expectZeroResults(requestEditItemFeature(itemA2X));
}
@Test
public void testDirectEPersonAdminPolicy() throws Exception {
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withUser(eperson)
.withDspaceObject(itemA1X)
.withAction(Constants.ADMIN)
.build();
expectSomeResults(requestSitewideEditItemFeature());
expectSomeResults(requestEditItemFeature(itemA1X));
expectZeroResults(requestEditItemFeature(itemA2X));
}
@Test
public void testDirectGroupAdminPolicy() throws Exception {
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withGroup(group)
.withDspaceObject(itemA1X)
.withAction(Constants.ADMIN)
.build();
expectSomeResults(requestSitewideEditItemFeature());
expectSomeResults(requestEditItemFeature(itemA1X));
expectZeroResults(requestEditItemFeature(itemA2X));
}
@Test
public void testNonemptyCollectionAdmin() throws Exception {
Item item = withSuppressedAuthorization(() -> {
Collection col = CollectionBuilder
.createCollection(context, communityA)
.withName("nonempty collection")
.withAdminGroup(eperson)
.build();
return ItemBuilder
.createItem(context, col)
.withTitle("item in nonempty collection")
.build();
});
expectSomeResults(requestSitewideEditItemFeature());
expectSomeResults(requestEditItemFeature(item));
expectZeroResults(requestEditItemFeature(itemA1X));
expectZeroResults(requestEditItemFeature(itemA2X));
}
@Test
public void testEmptyCollectionAdmin() throws Exception {
withSuppressedAuthorization(() -> {
Collection col = CollectionBuilder
.createCollection(context, communityA)
.withName("nonempty collection")
.withAdminGroup(eperson)
.build();
return null;
});
expectZeroResults(requestSitewideEditItemFeature());
}
@Test
public void testCommunityWithEmptyCollectionAdmin() throws Exception {
withSuppressedAuthorization(() -> {
Community comm = CommunityBuilder
.createCommunity(context)
.withName("This community contains a collection")
.withAdminGroup(eperson)
.build();
Collection coll = CollectionBuilder
.createCollection(context, comm)
.withName("This collection contains no items")
.build();
return null;
});
expectZeroResults(requestSitewideEditItemFeature());
}
@Test
public void testCommunityWithNonemptyCollectionAdmin() throws Exception {
Item item = withSuppressedAuthorization(() -> {
Community comm = CommunityBuilder
.createCommunity(context)
.withName("This community contains a collection")
.withAdminGroup(eperson)
.build();
Collection coll = CollectionBuilder
.createCollection(context, comm)
.withName("This collection contains an item")
.build();
return ItemBuilder
.createItem(context, coll)
.withTitle("This is an item")
.build();
});
expectSomeResults(requestSitewideEditItemFeature());
expectSomeResults(requestEditItemFeature(item));
}
@Test
public void testNestedCommunitiesWithNonemptyCollectionAdmin() throws Exception {
Item item = withSuppressedAuthorization(() -> {
Community parent = CommunityBuilder
.createCommunity(context)
.withName("parent community")
.withAdminGroup(eperson)
.build();
Community child = CommunityBuilder
.createSubCommunity(context, parent)
.withName("child community")
.withAdminGroup(eperson)
.build();
Collection coll = CollectionBuilder
.createCollection(context, child)
.withName("This collection contains an item")
.build();
return ItemBuilder
.createItem(context, coll)
.withTitle("This is an item")
.build();
});
expectSomeResults(requestSitewideEditItemFeature());
expectSomeResults(requestEditItemFeature(item));
}
private ResultActions requestSitewideEditItemFeature() throws Exception {
return requestEditItemFeature(siteUri);
}
private ResultActions requestEditItemFeature(Item item) throws Exception {
return requestEditItemFeature(getItemUri(item));
}
private ResultActions requestEditItemFeature(String uri) throws Exception {
epersonToken = getAuthToken(eperson.getEmail(), password);
return getClient(epersonToken).perform(get("/api/authz/authorizations/search/object?")
.param("uri", uri)
.param("feature", editItemFeature.getName())
.param("embed", "feature"));
}
private ResultActions expectSomeResults(ResultActions actions) throws Exception {
return actions
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.totalElements", greaterThan(0)));
}
private ResultActions expectZeroResults(ResultActions actions) throws Exception {
return actions
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.totalElements", is(0)));
}
private <T> T withSuppressedAuthorization(Callable<T> fn) throws Exception {
context.turnOffAuthorisationSystem();
T result = fn.call();
context.restoreAuthSystemState();
return result;
}
private String getItemUri(Item item) {
ItemRest itemRest = itemConverter.convert(item, DefaultProjection.DEFAULT);
return utils.linkToSingleResource(itemRest, "self").getHref();
}
}

View File

@@ -0,0 +1,328 @@
/**
* 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 java.util.concurrent.Callable;
import org.dspace.app.rest.authorization.impl.SubmitFeature;
import org.dspace.app.rest.converter.CollectionConverter;
import org.dspace.app.rest.converter.SiteConverter;
import org.dspace.app.rest.model.CollectionRest;
import org.dspace.app.rest.model.SiteRest;
import org.dspace.app.rest.projection.DefaultProjection;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.GroupBuilder;
import org.dspace.builder.ResourcePolicyBuilder;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Site;
import org.dspace.content.service.SiteService;
import org.dspace.core.Constants;
import org.dspace.eperson.Group;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.ResultActions;
public class SubmitFeatureIT extends AbstractControllerIntegrationTest {
@Autowired
private AuthorizationFeatureService authorizationFeatureService;
@Autowired
private SiteService siteService;
@Autowired
private CollectionConverter collectionConverter;
@Autowired
private SiteConverter siteConverter;
@Autowired
private Utils utils;
private Group group;
private String siteUri;
private String epersonToken;
private AuthorizationFeature submitFeature;
private Community communityA;
private Collection collectionA1;
private Collection collectionA2;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
withSuppressedAuthorization(() -> {
communityA = CommunityBuilder.createCommunity(context).withName("Community A").build();
collectionA1 = CollectionBuilder.createCollection(context, communityA).withName("Collection A1").build();
collectionA2 = CollectionBuilder.createCollection(context, communityA).withName("Collection A2").build();
group = GroupBuilder.createGroup(context)
.withName("group")
.addMember(eperson)
.build();
return null;
});
submitFeature = authorizationFeatureService.find(SubmitFeature.NAME);
Site site = siteService.findSite(context);
SiteRest siteRest = siteConverter.convert(site, DefaultProjection.DEFAULT);
siteUri = utils.linkToSingleResource(siteRest, "self").getHref();
}
@Test
public void testNoRights() throws Exception {
expectZeroResults(requestSitewideSubmitFeature());
}
@Test
public void testDirectEPersonAddPolicy() throws Exception {
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withUser(eperson)
.withDspaceObject(collectionA1)
.withAction(Constants.ADD)
.build();
expectSomeResults(requestSitewideSubmitFeature());
}
@Test
public void testDirectGroupAddPolicy() throws Exception {
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withGroup(group)
.withDspaceObject(collectionA1)
.withAction(Constants.ADD)
.build();
expectSomeResults(requestSitewideSubmitFeature());
}
@Test
public void testDirectEPersonAdminPolicy() throws Exception {
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withUser(eperson)
.withDspaceObject(collectionA1)
.withAction(Constants.ADMIN)
.build();
expectSomeResults(requestSitewideSubmitFeature());
}
@Test
public void testDirectGroupAdminPolicy() throws Exception {
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withGroup(group)
.withDspaceObject(collectionA1)
.withAction(Constants.ADMIN)
.build();
expectSomeResults(requestSitewideSubmitFeature());
}
@Test
public void testCollectionAdmin() throws Exception {
withSuppressedAuthorization(() -> {
Collection col = CollectionBuilder
.createCollection(context, communityA)
.withName("this is another test collection")
.withAdminGroup(eperson)
.build();
return null;
});
expectSomeResults(requestSitewideSubmitFeature());
}
@Test
public void testCommunityWithoutCollectionsAdmin() throws Exception {
withSuppressedAuthorization(() -> {
Community comm = CommunityBuilder
.createCommunity(context)
.withName("This community contains no collections")
.withAdminGroup(eperson)
.build();
return null;
});
expectZeroResults(requestSitewideSubmitFeature());
}
@Test
public void testCommunityWithCollectionsAdmin() throws Exception {
withSuppressedAuthorization(() -> {
Community comm = CommunityBuilder
.createCommunity(context)
.withName("This community contains a collection")
.withAdminGroup(eperson)
.build();
Collection coll = CollectionBuilder
.createCollection(context, comm)
.withName("Contained collection")
.build();
return null;
});
expectSomeResults(requestSitewideSubmitFeature());
}
@Test
public void testCommunityWithSubCommunityWithCollectionsAdmin() throws Exception {
withSuppressedAuthorization(() -> {
Community parent = CommunityBuilder
.createCommunity(context)
.withName("This community contains no collections")
.withAdminGroup(eperson)
.build();
Community child = CommunityBuilder
.createSubCommunity(context, parent)
.withName("This community contains a collection")
.build();
Collection coll = CollectionBuilder
.createCollection(context, child)
.withName("Contained collection")
.build();
return null;
});
expectSomeResults(requestSitewideSubmitFeature());
}
@Test
public void testNoRightsOnCollection() throws Exception {
expectZeroResults(requestSubmitFeature(getCollectionUri(collectionA1)));
expectZeroResults(requestSubmitFeature(getCollectionUri(collectionA2)));
}
@Test
public void testDirectEPersonAddPolicyOnCollection() throws Exception {
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withUser(eperson)
.withDspaceObject(collectionA1)
.withAction(Constants.ADD)
.build();
expectSomeResults(requestSubmitFeature(getCollectionUri(collectionA1)));
expectZeroResults(requestSubmitFeature(getCollectionUri(collectionA2)));
}
@Test
public void testDirectGroupAddPolicyOnCollection() throws Exception {
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withGroup(group)
.withDspaceObject(collectionA1)
.withAction(Constants.ADD)
.build();
expectSomeResults(requestSubmitFeature(getCollectionUri(collectionA1)));
expectZeroResults(requestSubmitFeature(getCollectionUri(collectionA2)));
}
@Test
public void testDirectEPersonAdminPolicyOnCollection() throws Exception {
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withUser(eperson)
.withDspaceObject(collectionA1)
.withAction(Constants.ADMIN)
.build();
expectSomeResults(requestSubmitFeature(getCollectionUri(collectionA1)));
expectZeroResults(requestSubmitFeature(getCollectionUri(collectionA2)));
}
@Test
public void testDirectGroupAdminPolicyOnCollection() throws Exception {
ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context)
.withGroup(group)
.withDspaceObject(collectionA1)
.withAction(Constants.ADMIN)
.build();
expectSomeResults(requestSubmitFeature(getCollectionUri(collectionA1)));
expectZeroResults(requestSubmitFeature(getCollectionUri(collectionA2)));
}
@Test
public void testCollectionAdminOnCollection() throws Exception {
Collection col = withSuppressedAuthorization(() -> {
return CollectionBuilder
.createCollection(context, communityA)
.withName("this is another test collection")
.withAdminGroup(eperson)
.build();
});
expectSomeResults(requestSubmitFeature(getCollectionUri(col)));
expectZeroResults(requestSubmitFeature(getCollectionUri(collectionA1)));
}
@Test
public void testCommunityWithCollectionsAdminOnCollection() throws Exception {
Collection coll = withSuppressedAuthorization(() -> {
Community comm = CommunityBuilder
.createCommunity(context)
.withName("This community contains a collection")
.withAdminGroup(eperson)
.build();
return CollectionBuilder
.createCollection(context, comm)
.withName("Contained collection")
.build();
});
expectSomeResults(requestSubmitFeature(getCollectionUri(coll)));
}
@Test
public void testCommunityWithSubCommunityWithCollectionsAdminOnCollection() throws Exception {
Collection coll = withSuppressedAuthorization(() -> {
Community parent = CommunityBuilder
.createCommunity(context)
.withName("This community contains no collections")
.withAdminGroup(eperson)
.build();
Community child = CommunityBuilder
.createSubCommunity(context, parent)
.withName("This community contains a collection")
.build();
return CollectionBuilder
.createCollection(context, child)
.withName("Contained collection")
.build();
});
expectSomeResults(requestSubmitFeature(getCollectionUri(coll)));
}
private ResultActions requestSitewideSubmitFeature() throws Exception {
return requestSubmitFeature(siteUri);
}
private ResultActions requestSubmitFeature(String uri) throws Exception {
epersonToken = getAuthToken(eperson.getEmail(), password);
return getClient(epersonToken).perform(get("/api/authz/authorizations/search/object?")
.param("uri", uri)
.param("feature", submitFeature.getName())
.param("embed", "feature"));
}
private ResultActions expectSomeResults(ResultActions actions) throws Exception {
return actions
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.totalElements", greaterThan(0)));
}
private ResultActions expectZeroResults(ResultActions actions) throws Exception {
return actions
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.totalElements", is(0)));
}
private <T> T withSuppressedAuthorization(Callable<T> fn) throws Exception {
context.turnOffAuthorisationSystem();
T result = fn.call();
context.restoreAuthSystemState();
return result;
}
private String getCollectionUri(Collection collection) {
CollectionRest collectionRest = collectionConverter.convert(collection, DefaultProjection.DEFAULT);
return utils.linkToSingleResource(collectionRest, "self").getHref();
}
}

View File

@@ -31,6 +31,7 @@
<bean id="solrServicePrivateItemPlugin" class="org.dspace.discovery.SolrServicePrivateItemPlugin" scope="prototype"/> <bean id="solrServicePrivateItemPlugin" class="org.dspace.discovery.SolrServicePrivateItemPlugin" scope="prototype"/>
<bean id="SolrServiceParentObjectIndexingPlugin" class="org.dspace.discovery.SolrServiceParentObjectIndexingPlugin" scope="prototype"/> <bean id="SolrServiceParentObjectIndexingPlugin" class="org.dspace.discovery.SolrServiceParentObjectIndexingPlugin" scope="prototype"/>
<bean id="SolrServiceIndexCollectionSubmittersPlugin" class="org.dspace.discovery.SolrServiceIndexCollectionSubmittersPlugin" scope="prototype"/> <bean id="SolrServiceIndexCollectionSubmittersPlugin" class="org.dspace.discovery.SolrServiceIndexCollectionSubmittersPlugin" scope="prototype"/>
<bean id="SolrServiceIndexItemEditorsPlugin" class="org.dspace.discovery.SolrServiceIndexItemEditorsPlugin" scope="prototype"/>
<alias name="solrServiceResourceIndexPlugin" alias="org.dspace.discovery.SolrServiceResourceRestrictionPlugin"/> <alias name="solrServiceResourceIndexPlugin" alias="org.dspace.discovery.SolrServiceResourceRestrictionPlugin"/>

View File

@@ -264,6 +264,9 @@
<!-- used to track which group(s) have submit permissions --> <!-- used to track which group(s) have submit permissions -->
<field name="submit" type="string" indexed="true" stored="true" omitNorms="true" multiValued="true" docValues="true" /> <field name="submit" type="string" indexed="true" stored="true" omitNorms="true" multiValued="true" docValues="true" />
<!-- used to track which eperson(s) have edit permissions on items -->
<field name="edit" type="string" indexed="true" stored="true" omitNorms="true" multiValued="true" docValues="true" />
<!-- used to track entity type of collections --> <!-- used to track entity type of collections -->
<field name="search.entitytype" type="string" indexed="true" stored="true" required="false" /> <field name="search.entitytype" type="string" indexed="true" stored="true" required="false" />