mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 01:54:22 +00:00
97183: Added SolrServiceIndexItemEditorsPlugin
Analogous to SolrServiceIndexCollectionSubmittersPlugin, but for Items instead of Collections, and for WRITE rights instead of ADD rights. Also refactored SolrServiceIndexCollectionSubmittersPlugin to share code with the new plugin.
This commit is contained in:
146
dspace-api/src/main/java/org/dspace/discovery/IndexingUtils.java
Normal file
146
dspace-api/src/main/java/org/dspace/discovery/IndexingUtils.java
Normal file
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* 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.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
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.Item;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* Util methods used by indexing.
|
||||
*
|
||||
* @author Koen Pauwels (koen.pauwels at atmire dot com)
|
||||
*/
|
||||
public abstract class IndexingUtils {
|
||||
/**
|
||||
* Retrieve all ancestor communities of a given community, with the first one being the given community and the
|
||||
* last one being the root.
|
||||
*
|
||||
* TODO: can be done in a single SQL query with recursive common table expressions
|
||||
* TODO: should probably be moved to CommunityService
|
||||
*
|
||||
* @param context DSpace context object
|
||||
* @param community Community for which we search the ancestors
|
||||
* @return A stream of ancestor communities.
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
static Stream<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.stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 stream of admin group IDs
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
static Stream<UUID> findTransitiveAdminGroupIds(Context context, Community community) throws SQLException {
|
||||
return getAncestorCommunities(context, community)
|
||||
.map(parent -> parent.getAdministrators().getID());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 stream of admin group IDs
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
static Stream<UUID> findTransitiveAdminGroupIds(Context context, Collection collection) throws SQLException {
|
||||
UUID directAdminGroupId = collection.getAdministrators().getID();
|
||||
List<Stream<UUID>> subResults = Arrays.asList(Stream.of(directAdminGroupId));
|
||||
for (Community community : collection.getCommunities()) {
|
||||
subResults.add(findTransitiveAdminGroupIds(context, community));
|
||||
}
|
||||
return sequence(subResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the ids of all groups that have ADMIN rights on the given item, either directly
|
||||
* (through direct resource policy) or indirectly (through a policy on the owning collection, or on
|
||||
* the owning collection's community, or on any of that community's ancestor communities).
|
||||
*
|
||||
* @param authService
|
||||
* @param context
|
||||
* @param item
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*
|
||||
* @param authService The authentication service
|
||||
* @param context DSpace context object
|
||||
* @param item Item for which we search the admin group IDs
|
||||
* @return A stream of admin group IDs
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
static Stream<UUID> findTransitiveAdminGroupIds(AuthorizeService authService, Context context, Item item)
|
||||
throws SQLException {
|
||||
Stream<UUID> directAdminGroupIds = authService.getPoliciesActionFilter(context, item, Constants.ADMIN)
|
||||
.stream()
|
||||
.filter(policy -> policy.getGroup() != null)
|
||||
.map(policy -> policy.getGroup().getID());
|
||||
List<Stream<UUID>> subResults = Arrays.asList(directAdminGroupIds);
|
||||
for (Collection coll : item.getCollections()) {
|
||||
subResults.add(findTransitiveAdminGroupIds(context, coll));
|
||||
}
|
||||
return sequence(subResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Stream<String> findDirectAuthorizedGroupsAndEPersonsPrefixedIds(AuthorizeService authService,
|
||||
Context context, DSpaceObject obj, int[] authorizations) throws SQLException {
|
||||
|
||||
ArrayList<Stream<String>> subResults = new ArrayList<>();
|
||||
for (int auth : authorizations) {
|
||||
Stream<String> subResult = authService.getPoliciesActionFilter(context, obj, auth).stream()
|
||||
.map(policy -> policy.getGroup() == null ? "e" + policy.getEPerson().getID()
|
||||
: "g" + policy.getGroup().getID());
|
||||
subResults.add(subResult);
|
||||
// TODO: context.uncacheEntitiy(policy);
|
||||
}
|
||||
return sequence(subResults);
|
||||
}
|
||||
|
||||
private static <T> Stream<T> sequence(List<Stream<T>> subResults) {
|
||||
return subResults.stream().flatMap(x -> x);
|
||||
}
|
||||
}
|
@@ -8,15 +8,11 @@
|
||||
package org.dspace.discovery;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.solr.common.SolrInputDocument;
|
||||
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.factory.ContentServiceFactory;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.LogHelper;
|
||||
@@ -42,30 +38,18 @@ public class SolrServiceIndexCollectionSubmittersPlugin implements SolrServiceIn
|
||||
Collection col = ((IndexableCollection) idxObj).getIndexedObject();
|
||||
if (col != null) {
|
||||
try {
|
||||
String fieldValue = null;
|
||||
Community parent = (Community) ContentServiceFactory.getInstance().getDSpaceObjectService(col)
|
||||
.getParentObject(context, col);
|
||||
while (parent != null) {
|
||||
if (parent.getAdministrators() != null) {
|
||||
fieldValue = "g" + parent.getAdministrators().getID();
|
||||
document.addField("submit", fieldValue);
|
||||
}
|
||||
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));
|
||||
// Index groups with ADMIN rights on the Collection, on
|
||||
// Communities containing those Collections, and recursively on any Community containing such 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.
|
||||
IndexingUtils.findTransitiveAdminGroupIds(context, col)
|
||||
.forEach(unprefixedId -> document.addField("submit", "g" + unprefixedId));
|
||||
|
||||
for (ResourcePolicy resourcePolicy : policies) {
|
||||
if (resourcePolicy.getGroup() != null) {
|
||||
fieldValue = "g" + resourcePolicy.getGroup().getID();
|
||||
} else {
|
||||
fieldValue = "e" + resourcePolicy.getEPerson().getID();
|
||||
|
||||
}
|
||||
document.addField("submit", fieldValue);
|
||||
context.uncacheEntity(resourcePolicy);
|
||||
}
|
||||
// Index groups and epersons with ADD rights on the Collection.
|
||||
IndexingUtils.findDirectAuthorizedGroupsAndEPersonsPrefixedIds(
|
||||
authorizeService, context, col, new int[] {Constants.ADD}
|
||||
).forEach(prefixedId -> document.addField("submit", prefixedId));
|
||||
} catch (SQLException e) {
|
||||
log.error(LogHelper.getHeader(context, "Error while indexing resource policies",
|
||||
"Collection: (id " + col.getID() + " type " + col.getName() + ")" ));
|
||||
@@ -74,4 +58,4 @@ public class SolrServiceIndexCollectionSubmittersPlugin implements SolrServiceIn
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.discovery;
|
||||
|
||||
import static org.dspace.discovery.IndexingUtils.findDirectAuthorizedGroupsAndEPersonsPrefixedIds;
|
||||
import static org.dspace.discovery.IndexingUtils.findTransitiveAdminGroupIds;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.solr.common.SolrInputDocument;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
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(SolrServiceIndexCollectionSubmittersPlugin.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 the Item, 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.
|
||||
findTransitiveAdminGroupIds(authorizeService, context, item)
|
||||
.forEach(unprefixedId -> document.addField("edit", "g" + unprefixedId));
|
||||
|
||||
// Index groups and epersons with WRITE rights on the Item.
|
||||
findDirectAuthorizedGroupsAndEPersonsPrefixedIds(
|
||||
authorizeService, context, item, new int[] {Constants.WRITE}
|
||||
).forEach(prefixedId -> document.addField("edit", prefixedId));
|
||||
|
||||
} catch (SQLException e) {
|
||||
log.error(LogHelper.getHeader(context, "Error while indexing resource policies",
|
||||
"Item: (id " + item.getID() + " name " + item.getName() + ")" ));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -13,6 +13,7 @@ import java.util.List;
|
||||
import org.dspace.app.rest.model.BaseObjectRest;
|
||||
import org.dspace.app.rest.model.SiteRest;
|
||||
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
|
||||
@@ -34,7 +35,8 @@ public interface AuthorizationFeatureService {
|
||||
* feature pass the {@link SiteRest} object
|
||||
* @return true if the user associated with the context has access to the feature
|
||||
*/
|
||||
boolean isAuthorized(Context context, AuthorizationFeature feature, BaseObjectRest object) throws SQLException;
|
||||
boolean isAuthorized(Context context, AuthorizationFeature feature, BaseObjectRest object)
|
||||
throws SQLException, SearchServiceException;
|
||||
|
||||
/**
|
||||
* Get all the authorization features defined in the system
|
||||
@@ -60,4 +62,4 @@ public interface AuthorizationFeatureService {
|
||||
* @return
|
||||
*/
|
||||
List<AuthorizationFeature> findByResourceType(String categoryDotModel);
|
||||
}
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@ import org.dspace.app.rest.authorization.AuthorizationFeatureService;
|
||||
import org.dspace.app.rest.model.BaseObjectRest;
|
||||
import org.dspace.app.rest.utils.Utils;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.discovery.SearchServiceException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -38,7 +39,7 @@ public class AuthorizationFeatureServiceImpl implements AuthorizationFeatureServ
|
||||
|
||||
@Override
|
||||
public boolean isAuthorized(Context context, AuthorizationFeature feature, BaseObjectRest object)
|
||||
throws SQLException {
|
||||
throws SQLException, SearchServiceException {
|
||||
if (object == null) {
|
||||
// the authorization interface require that the object is not null
|
||||
return false;
|
||||
|
@@ -31,6 +31,7 @@ import org.dspace.app.rest.model.BaseObjectRest;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.discovery.SearchServiceException;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.eperson.service.EPersonService;
|
||||
import org.slf4j.Logger;
|
||||
@@ -124,7 +125,7 @@ public class AuthorizationRestRepository extends DSpaceRestRepository<Authorizat
|
||||
// restore the real current user
|
||||
context.restoreContextUser();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
} catch (SQLException | SearchServiceException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
@@ -151,7 +152,7 @@ public class AuthorizationRestRepository extends DSpaceRestRepository<Authorizat
|
||||
@SearchRestMethod(name = "object")
|
||||
public Page<AuthorizationRest> findByObject(@Parameter(value = "uri", required = true) String uri,
|
||||
@Parameter(value = "eperson") UUID epersonUuid, @Parameter(value = "feature") String featureName,
|
||||
Pageable pageable) throws AuthorizeException, SQLException {
|
||||
Pageable pageable) throws AuthorizeException, SQLException, SearchServiceException {
|
||||
|
||||
Context context = obtainContext();
|
||||
|
||||
@@ -234,7 +235,7 @@ public class AuthorizationRestRepository extends DSpaceRestRepository<Authorizat
|
||||
Context context,
|
||||
EPerson user,
|
||||
String uri,
|
||||
String featureName) throws SQLException {
|
||||
String featureName) throws SQLException, SearchServiceException {
|
||||
|
||||
BaseObjectRest restObject = utils.getBaseObjectRestFromUri(context, uri);
|
||||
return authorizationsForObject(context, user, featureName, restObject);
|
||||
@@ -244,7 +245,7 @@ public class AuthorizationRestRepository extends DSpaceRestRepository<Authorizat
|
||||
Context context,
|
||||
EPerson user, String featureName,
|
||||
BaseObjectRest obj)
|
||||
throws SQLException {
|
||||
throws SQLException, SearchServiceException {
|
||||
|
||||
if (obj == null) {
|
||||
return new ArrayList<>();
|
||||
@@ -269,7 +270,7 @@ public class AuthorizationRestRepository extends DSpaceRestRepository<Authorizat
|
||||
|
||||
private List<Authorization> findByObjectAndFeature(
|
||||
Context context, EPerson user, BaseObjectRest obj, String featureName
|
||||
) throws SQLException {
|
||||
) throws SQLException, SearchServiceException {
|
||||
|
||||
AuthorizationFeature feature = authorizationFeatureService.find(featureName);
|
||||
|
||||
|
@@ -264,6 +264,9 @@
|
||||
<!-- used to track which group(s) have submit permissions -->
|
||||
<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 -->
|
||||
<field name="search.entitytype" type="string" indexed="true" stored="true" required="false" />
|
||||
|
||||
|
Reference in New Issue
Block a user