CST-10635 merge conflicts + IT test fix

This commit is contained in:
frabacche
2023-11-15 10:29:37 +01:00
25 changed files with 979 additions and 293 deletions

View File

@@ -0,0 +1,49 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.qaevent.security;
import java.sql.SQLException;
import java.util.Optional;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.QAEvent;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.springframework.beans.factory.annotation.Autowired;
/**
* QASecurity that restrict access to the QA Source and related events only to repository administrators
*
* @author Andrea Bollini (andrea.bollini at 4science.com)
*
*/
public class AdministratorsOnlyQASecurity implements QASecurity {
@Autowired
private AuthorizeService authorizeService;
public Optional<String> generateFilterQuery(Context context, EPerson currentUser) {
return Optional.empty();
}
public boolean canSeeQASource(Context context, EPerson user) {
try {
return authorizeService.isAdmin(context, user);
} catch (SQLException e) {
return false;
}
}
public boolean canSeeQAEvent(Context context, EPerson user, QAEvent qaEvent) {
try {
return authorizeService.isAdmin(context, user);
} catch (SQLException e) {
return false;
}
}
}

View File

@@ -0,0 +1,53 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.qaevent.security;
import java.util.Optional;
import org.dspace.content.QAEvent;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
/**
*
*
* @author Andrea Bollini (andrea.bollini at 4science.com)
*
*/
public interface QASecurity {
/**
* Return a SOLR queries that can be applied querying the qaevent SOLR core to retrieve only the qaevents visible to
* the provided user
*
* @param context the DSpace context
* @param user the user to consider to restrict the visible qaevents
* @return the SOLR filter query to apply
*/
public Optional<String> generateFilterQuery(Context context, EPerson user);
/**
* Return <code>true</code> it the user is potentially allowed to see events in the qasource that adopt this
* security strategy
*
* @param context the DSpace context
* @param user the user to consider to restrict the visible qaevents
* @return <code>true</code> if the user can eventually see some qaevents
*/
public boolean canSeeQASource(Context context, EPerson user);
/**
* Return <code>true</code> it the user is potentially allowed to see events in the qasource that adopt this
* security strategy
*
* @param context the DSpace context
* @param user the user to consider to restrict the visible qaevents
* @return <code>true</code> if the user can see the provided qaEvent
*/
public boolean canSeeQAEvent(Context context, EPerson user, QAEvent qaEvent);
}

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.qaevent.security;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.Optional;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.QAEvent;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.qaevent.service.QAEventService;
import org.springframework.beans.factory.annotation.Autowired;
/**
* QASecurity implementations that allow access to only qa events that match a SORL query generated using the eperson
* uuid
*
* @author Andrea Bollini (andrea.bollini at 4science.com)
*
*/
public class UserBasedFilterQASecurity implements QASecurity {
@Autowired
private AuthorizeService authorizeService;
@Autowired
private QAEventService qaEventService;
private String filterTemplate;
private boolean allowAdmins = true;
public Optional<String> generateFilterQuery(Context context, EPerson user) {
try {
if (allowAdmins && authorizeService.isAdmin(context, user)) {
return Optional.empty();
} else {
return Optional.of(MessageFormat.format(filterTemplate, user.getID().toString()));
}
} catch (SQLException e) {
throw new RuntimeException("Error checking permissions", e);
}
}
public boolean canSeeQASource(Context context, EPerson user) {
return user != null;
}
public boolean canSeeQAEvent(Context context, EPerson user, QAEvent qaEvent) {
try {
return (allowAdmins && authorizeService.isAdmin(context, user))
|| qaEventService.qaEventsInSource(context, user, qaEvent.getEventId(), qaEvent.getSource());
} catch (SQLException e) {
throw new RuntimeException("Error checking permissions", e);
}
}
public void setFilterTemplate(String filterTemplate) {
this.filterTemplate = filterTemplate;
}
public void setAllowAdmins(boolean allowAdmins) {
this.allowAdmins = allowAdmins;
}
}

View File

@@ -0,0 +1,55 @@
/**
* 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.qaevent.service;
import java.util.Optional;
import org.dspace.content.QAEvent;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
/**
* Interface to limit the visibility of {@link QAEvent} to specific users.
*
* @author Andrea Bollini (andrea.bollini at 4science.com)
*
*/
public interface QAEventSecurityService {
/**
* Check if the specified user can see a specific QASource
* @param context the context
* @param user the eperson to consider
* @param qaSource the qaSource involved
* @return <code>true</code> if the specified user can eventually see events in the QASource
*/
boolean canSeeSource(Context context, EPerson user, String qaSource);
/**
* Check if the specified user can see a specific QAEvent. It is expected that a QAEvent in a not visible QASource
* cannot be accessed. So implementation of this method should enforce this rule.
*
* @param context the context
* @param user the eperson to consider
* @param qaEvent the qaevent to check
* @return <code>true</code> if the specified user can see the specified event
*/
boolean canSeeEvent(Context context, EPerson user, QAEvent qaEvent);
/**
* Generate a query to restrict the qa events returned by other search/find method to the only ones visible to the
* specified user
*
* @param context the context
* @param user the eperson to consider
* @param qaSource the qaSource involved
* @return the solr filter query
*/
public Optional<String> generateQAEventFilterQuery(Context context, EPerson user, String qaSource);
}

View File

@@ -12,6 +12,7 @@ import java.util.UUID;
import org.dspace.content.QAEvent;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.qaevent.QASource;
import org.dspace.qaevent.QATopic;
@@ -26,57 +27,64 @@ public interface QAEventService {
/**
* Find all the event's topics related to the given source.
*
* @param context the DSpace context
* @param source the source to search for
* @param offset the offset to apply
* @param count the page size
* @return the topics list
*/
public List<QATopic> findAllTopicsBySource(String source, long offset, int count);
public List<QATopic> findAllTopicsBySource(Context context, String source, long offset, int count);
/**
* Find a specific topic by its name, source and optionally a target.
*
* @param context the DSpace context
* @param sourceName the name of the source
* @param topicName the topic name to search for
* @param target (nullable) the uuid of the target to focus on
* @return the topic
*/
public QATopic findTopicBySourceAndNameAndTarget(String sourceName, String topicName, UUID target);
public QATopic findTopicBySourceAndNameAndTarget(Context context, String sourceName, String topicName, UUID target);
/**
* Count all the event's topics related to the given source.
*
* @param context the DSpace context
* @param source the source to search for
* @return the count result
*/
public long countTopicsBySource(String source);
public long countTopicsBySource(Context context, String source);
/**
* Find all the events by topic sorted by trust descending.
*
* @param context the DSpace context
* @param source the source name
* @param topic the topic to search for
* @param offset the offset to apply
* @param pageSize the page size
* @return the events
*/
public List<QAEvent> findEventsByTopicAndPage(String source, String topic, long offset, int pageSize);
public List<QAEvent> findEventsByTopicAndPage(Context context, String source, String topic, long offset,
int pageSize);
/**
* Find all the events by topic.
*
* @param context the DSpace context
* @param topic the topic to search for
* @return the events count
*/
public long countEventsByTopic(String source, String topic);
public long countEventsByTopic(Context context, String source, String topic);
/**
* Find an event by the given id.
* Find an event by the given id. Please note that no security filter are applied by this method.
*
* @param context the DSpace context
* @param id the id of the event to search for
* @return the event
*/
public QAEvent findEventByEventId(String id);
public QAEvent findEventByEventId(Context context, String id);
/**
* Store the given event.
@@ -103,35 +111,39 @@ public interface QAEventService {
/**
* Find a specific source by the given name.
*
* @param source the source name
* @return the source
* @param context the DSpace context
* @param source the source name
* @return the source
*/
public QASource findSource(String source);
public QASource findSource(Context context, String source);
/**
* Find a specific source by the given name including the stats focused on the target item.
*
* @param source the source name
* @param target the uuid of the item target
* @param context the DSpace context
* @param source the source name
* @param target the uuid of the item target
* @return the source
*/
public QASource findSource(String source, UUID target);
public QASource findSource(Context context, String source, UUID target);
/**
* Find all the event's sources.
*
* @param context the DSpace context
* @param offset the offset to apply
* @param pageSize the page size
* @return the sources list
*/
public List<QASource> findAllSources(long offset, int pageSize);
public List<QASource> findAllSources(Context context, long offset, int pageSize);
/**
* Count all the event's sources.
*
* @param context the DSpace context
* @return the count result
*/
public long countSources();
public long countSources(Context context);
/**
* Check if the given QA event supports a related item.
@@ -145,62 +157,80 @@ public interface QAEventService {
* Find a list of QA events according to the pagination parameters for the specified topic and target sorted by
* trust descending
*
* @param source the source name
* @param topic the topic to search for
* @param offset the offset to apply
* @param pageSize the page size
* @param target the uuid of the QA event's target
* @param context the DSpace context
* @param source the source name
* @param topic the topic to search for
* @param offset the offset to apply
* @param pageSize the page size
* @param target the uuid of the QA event's target
* @return the events
*/
public List<QAEvent> findEventsByTopicAndPageAndTarget(String source, String topic, long offset, int pageSize,
UUID target);
public List<QAEvent> findEventsByTopicAndPageAndTarget(Context context, String source, String topic, long offset,
int pageSize, UUID target);
/**
* Count the QA events related to the specified topic and target
*
* @param source the source name
* @param topic the topic to search for
* @param target the uuid of the QA event's target
*
* @param context the DSpace context
* @param source the source name
* @param topic the topic to search for
* @param target the uuid of the QA event's target
* @return the count result
*/
public long countEventsByTopicAndTarget(String source, String topic, UUID target);
public long countEventsByTopicAndTarget(Context context, String source, String topic, UUID target);
/**
* Find all the event's topics related to the given source for a specific item
*
* @param context the DSpace context
* @param source (not null) the source to search for
* @param target the item referring to
* @param offset the offset to apply
* @param pageSize the page size
* @return the topics list
*/
public List<QATopic> findAllTopicsBySourceAndTarget(String source, UUID target, long offset, int pageSize);
public List<QATopic> findAllTopicsBySourceAndTarget(Context context, String source, UUID target, long offset,
int pageSize);
/**
* Count all the event's topics related to the given source referring to a specific item
*
* @param context the DSpace context
* @param target the item uuid
* @param source the source to search for
* @return the count result
*/
public long countTopicsBySourceAndTarget(String source, UUID target);
public long countTopicsBySourceAndTarget(Context context, String source, UUID target);
/**
* Find all the event's sources related to a specific item
*
* @param context the DSpace context
* @param target the item referring to
* @param offset the offset to apply
* @param pageSize the page size
* @return the source list
*/
public List<QASource> findAllSourcesByTarget(UUID target, long offset, int pageSize);
public List<QASource> findAllSourcesByTarget(Context context, UUID target, long offset, int pageSize);
/**
* Count all the event's sources related to a specific item
*
* @param context the DSpace context
* @param target the item uuid
* @return the count result
*/
public long countSourcesByTarget(UUID target);
public long countSourcesByTarget(Context context, UUID target);
/**
* Check if a qaevent with the provided id is visible to the current user according to the source security
*
* @param context the DSpace context
* @param user the user to consider for the security check
* @param eventId the id of the event to check for existence
* @param source the qa source name
* @return <code>true</code> if the event exists
*/
public boolean qaEventsInSource(Context context, EPerson user, String eventId, String source);
}

View File

@@ -7,6 +7,7 @@
*/
package org.dspace.qaevent.service.dto;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
@@ -15,6 +16,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
*
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class OpenaireMessageDTO implements QAMessageDTO {
@JsonProperty("pids[0].value")

View File

@@ -0,0 +1,60 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.qaevent.service.impl;
import java.util.Map;
import java.util.Optional;
import org.apache.logging.log4j.Logger;
import org.dspace.content.QAEvent;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.qaevent.security.QASecurity;
import org.dspace.qaevent.service.QAEventSecurityService;
public class QAEventSecurityServiceImpl implements QAEventSecurityService {
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(QAEventSecurityServiceImpl.class);
private Map<String, QASecurity> qaSecurityConfiguration;
private QASecurity defaultSecurity;
public void setQaSecurityConfiguration(Map<String, QASecurity> qaSecurityConfiguration) {
this.qaSecurityConfiguration = qaSecurityConfiguration;
}
public void setDefaultSecurity(QASecurity defaultSecurity) {
this.defaultSecurity = defaultSecurity;
}
@Override
public Optional<String> generateQAEventFilterQuery(Context context, EPerson user, String qaSource) {
QASecurity qaSecurity = getQASecurity(qaSource);
return qaSecurity.generateFilterQuery(context, user);
}
private QASecurity getQASecurity(String qaSource) {
return qaSecurityConfiguration.getOrDefault(qaSource, defaultSecurity);
}
@Override
public boolean canSeeEvent(Context context, EPerson user, QAEvent qaEvent) {
String source = qaEvent.getSource();
QASecurity qaSecurity = getQASecurity(source);
return qaSecurity.canSeeQASource(context, user)
&& qaSecurity.canSeeQAEvent(context, user, qaEvent);
}
@Override
public boolean canSeeSource(Context context, EPerson user, String qaSource) {
QASecurity qaSecurity = getQASecurity(qaSource);
return qaSecurity.canSeeQASource(context, user);
}
}

View File

@@ -17,6 +17,8 @@ import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -42,6 +44,7 @@ import org.dspace.content.Item;
import org.dspace.content.QAEvent;
import org.dspace.content.service.ItemService;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.handle.service.HandleService;
import org.dspace.qaevent.AutomaticProcessingAction;
import org.dspace.qaevent.QAEventAutomaticProcessingEvaluation;
@@ -50,6 +53,7 @@ import org.dspace.qaevent.QATopic;
import org.dspace.qaevent.dao.QAEventsDao;
import org.dspace.qaevent.dao.impl.QAEventsDaoImpl;
import org.dspace.qaevent.service.QAEventActionService;
import org.dspace.qaevent.service.QAEventSecurityService;
import org.dspace.qaevent.service.QAEventService;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
@@ -72,6 +76,9 @@ public class QAEventServiceImpl implements QAEventService {
@Autowired(required = true)
protected ConfigurationService configurationService;
@Autowired(required = true)
protected QAEventSecurityService qaSecurityService;
@Autowired(required = true)
protected ItemService itemService;
@@ -81,7 +88,7 @@ public class QAEventServiceImpl implements QAEventService {
@Autowired
private QAEventsDaoImpl qaEventsDao;
@Autowired
@Autowired(required = false)
@Qualifier("qaAutomaticProcessingMap")
private Map<String, QAEventAutomaticProcessingEvaluation> qaAutomaticProcessingMap;
@@ -121,10 +128,16 @@ public class QAEventServiceImpl implements QAEventService {
}
@Override
public long countTopicsBySource(String source) {
public long countTopicsBySource(Context context, String source) {
if (isNotSupportedSource(source)
|| !qaSecurityService.canSeeSource(context, context.getCurrentUser(), source)) {
return 0;
}
SolrQuery solrQuery = new SolrQuery();
solrQuery.setRows(0);
solrQuery.setQuery("*:*");
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context,
context.getCurrentUser(), source);
solrQuery.setQuery(securityQuery.orElse("*:*"));
solrQuery.setFacet(true);
solrQuery.setFacetMinCount(1);
solrQuery.addFacetField(TOPIC);
@@ -139,10 +152,16 @@ public class QAEventServiceImpl implements QAEventService {
}
@Override
public long countTopicsBySourceAndTarget(String source, UUID target) {
public long countTopicsBySourceAndTarget(Context context, String source, UUID target) {
if (isNotSupportedSource(source)
|| !qaSecurityService.canSeeSource(context, context.getCurrentUser(), source)) {
return 0;
}
SolrQuery solrQuery = new SolrQuery();
solrQuery.setRows(0);
solrQuery.setQuery("*:*");
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context,
context.getCurrentUser(), source);
solrQuery.setQuery(securityQuery.orElse("*:*"));
solrQuery.setFacet(true);
solrQuery.setFacetMinCount(1);
solrQuery.addFacetField(TOPIC);
@@ -180,15 +199,22 @@ public class QAEventServiceImpl implements QAEventService {
}
@Override
public QATopic findTopicBySourceAndNameAndTarget(String sourceName, String topicName, UUID target) {
public QATopic findTopicBySourceAndNameAndTarget(Context context, String sourceName, String topicName,
UUID target) {
if (isNotSupportedSource(sourceName)
|| !qaSecurityService.canSeeSource(context, context.getCurrentUser(), sourceName)) {
return null;
}
SolrQuery solrQuery = new SolrQuery();
solrQuery.setRows(0);
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context,
context.getCurrentUser(), sourceName);
solrQuery.setQuery(securityQuery.orElse("*:*"));
solrQuery.addFilterQuery(SOURCE + ":\"" + sourceName + "\"");
solrQuery.addFilterQuery(TOPIC + ":\"" + topicName + "\"");
if (target != null) {
solrQuery.setQuery(RESOURCE_UUID + ":\"" + target.toString() + "\"");
} else {
solrQuery.setQuery("*:*");
solrQuery.addFilterQuery(RESOURCE_UUID + ":\"" + target.toString() + "\"");
}
solrQuery.setFacet(true);
solrQuery.setFacetMinCount(1);
@@ -214,18 +240,22 @@ public class QAEventServiceImpl implements QAEventService {
}
@Override
public List<QATopic> findAllTopicsBySource(String source, long offset, int count) {
return findAllTopicsBySourceAndTarget(source, null, offset, count);
public List<QATopic> findAllTopicsBySource(Context context, String source, long offset, int count) {
return findAllTopicsBySourceAndTarget(context, source, null, offset, count);
}
@Override
public List<QATopic> findAllTopicsBySourceAndTarget(String source, UUID target, long offset, int count) {
if (source != null && isNotSupportedSource(source)) {
return null;
public List<QATopic> findAllTopicsBySourceAndTarget(Context context, String source, UUID target, long offset,
int count) {
if (isNotSupportedSource(source)
|| !qaSecurityService.canSeeSource(context, context.getCurrentUser(), source)) {
return List.of();
}
SolrQuery solrQuery = new SolrQuery();
solrQuery.setRows(0);
solrQuery.setQuery("*:*");
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context,
context.getCurrentUser(), source);
solrQuery.setQuery(securityQuery.orElse("*:*"));
solrQuery.setFacet(true);
solrQuery.setFacetMinCount(1);
solrQuery.setFacetLimit((int) (offset + count));
@@ -322,8 +352,9 @@ public class QAEventServiceImpl implements QAEventService {
}
@Override
public QAEvent findEventByEventId(String eventId) {
SolrQuery param = new SolrQuery(EVENT_ID + ":" + eventId);
public QAEvent findEventByEventId(Context context, String eventId) {
SolrQuery param = new SolrQuery("*:*");
param.addFilterQuery(EVENT_ID + ":\"" + eventId + "\"");
QueryResponse response;
try {
response = getSolr().query(param);
@@ -341,8 +372,31 @@ public class QAEventServiceImpl implements QAEventService {
}
@Override
public List<QAEvent> findEventsByTopicAndPage(String source, String topic, long offset, int pageSize) {
public boolean qaEventsInSource(Context context, EPerson user, String eventId, String source) {
SolrQuery solrQuery = new SolrQuery();
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context,
user, source);
solrQuery.setQuery(securityQuery.orElse("*:*"));
solrQuery.addFilterQuery(EVENT_ID + ":\"" + eventId + "\"");
QueryResponse response;
try {
response = getSolr().query(solrQuery);
if (response != null) {
return response.getResults().getNumFound() == 1;
}
} catch (SolrServerException | IOException e) {
throw new RuntimeException("Exception querying Solr", e);
}
return false;
}
@Override
public List<QAEvent> findEventsByTopicAndPage(Context context, String source, String topic, long offset,
int pageSize) {
if (isNotSupportedSource(source)
|| !qaSecurityService.canSeeSource(context, context.getCurrentUser(), source)) {
return List.of();
}
SolrQuery solrQuery = new SolrQuery();
solrQuery.setStart(((Long) offset).intValue());
if (pageSize != -1) {
@@ -372,17 +426,21 @@ public class QAEventServiceImpl implements QAEventService {
}
@Override
public List<QAEvent> findEventsByTopicAndPageAndTarget(String source, String topic, long offset, int pageSize,
UUID target) {
public List<QAEvent> findEventsByTopicAndPageAndTarget(Context context, String source, String topic, long offset,
int pageSize, UUID target) {
if (isNotSupportedSource(source)
|| !qaSecurityService.canSeeSource(context, context.getCurrentUser(), source)) {
return List.of();
}
SolrQuery solrQuery = new SolrQuery();
solrQuery.setStart(((Long) offset).intValue());
solrQuery.setRows(pageSize);
solrQuery.setSort(TRUST, ORDER.desc);
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context,
context.getCurrentUser(), source);
solrQuery.setQuery(securityQuery.orElse("*:*"));
if (target != null) {
solrQuery.setQuery(RESOURCE_UUID + ":\"" + target.toString() + "\"");
} else {
solrQuery.setQuery("*:*");
solrQuery.addFilterQuery(RESOURCE_UUID + ":\"" + target.toString() + "\"");
}
solrQuery.addFilterQuery(SOURCE + ":\"" + source + "\"");
solrQuery.addFilterQuery(TOPIC + ":\"" + topic + "\"");
@@ -407,10 +465,17 @@ public class QAEventServiceImpl implements QAEventService {
}
@Override
public long countEventsByTopic(String source, String topic) {
public long countEventsByTopic(Context context, String source, String topic) {
if (isNotSupportedSource(source)
|| !qaSecurityService.canSeeSource(context, context.getCurrentUser(), source)) {
return 0;
}
SolrQuery solrQuery = new SolrQuery();
solrQuery.setRows(0);
solrQuery.setQuery(TOPIC + ":\"" + topic + "\"");
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context, context.getCurrentUser(),
source);
solrQuery.setQuery(securityQuery.orElse("*:*"));
solrQuery.addFilterQuery(TOPIC + ":\"" + topic + "\"");
solrQuery.addFilterQuery(SOURCE + ":\"" + source + "\"");
QueryResponse response = null;
try {
@@ -422,13 +487,18 @@ public class QAEventServiceImpl implements QAEventService {
}
@Override
public long countEventsByTopicAndTarget(String source, String topic, UUID target) {
public long countEventsByTopicAndTarget(Context context, String source, String topic, UUID target) {
if (isNotSupportedSource(source)
|| !qaSecurityService.canSeeSource(context, context.getCurrentUser(), source)) {
return 0;
}
SolrQuery solrQuery = new SolrQuery();
solrQuery.setRows(0);
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context, context.getCurrentUser(),
source);
solrQuery.setQuery(securityQuery.orElse("*:*"));
if (target != null) {
solrQuery.setQuery(RESOURCE_UUID + ":\"" + target.toString() + "\"");
} else {
solrQuery.setQuery("*:*");
solrQuery.addFilterQuery(RESOURCE_UUID + ":\"" + target.toString() + "\"");
}
solrQuery.addFilterQuery(SOURCE + ":\"" + source + "\"");
solrQuery.addFilterQuery(TOPIC + ":\"" + topic + "\"");
@@ -442,19 +512,23 @@ public class QAEventServiceImpl implements QAEventService {
}
@Override
public QASource findSource(String sourceName) {
public QASource findSource(Context context, String sourceName) {
String[] split = sourceName.split(":");
return findSource(split[0], split.length == 2 ? UUID.fromString(split[1]) : null);
return findSource(context, split[0], split.length == 2 ? UUID.fromString(split[1]) : null);
}
@Override
public QASource findSource(String sourceName, UUID target) {
public QASource findSource(Context context, String sourceName, UUID target) {
if (isNotSupportedSource(sourceName)) {
if (isNotSupportedSource(sourceName)
|| !qaSecurityService.canSeeSource(context, context.getCurrentUser(), sourceName)) {
return null;
}
SolrQuery solrQuery = new SolrQuery("*:*");
SolrQuery solrQuery = new SolrQuery();
Optional<String> securityQuery = qaSecurityService.generateQAEventFilterQuery(context, context.getCurrentUser(),
sourceName);
solrQuery.setQuery(securityQuery.orElse("*:*"));
solrQuery.setRows(0);
solrQuery.addFilterQuery(SOURCE + ":\"" + sourceName + "\"");
if (target != null) {
@@ -490,9 +564,10 @@ public class QAEventServiceImpl implements QAEventService {
}
@Override
public List<QASource> findAllSources(long offset, int pageSize) {
public List<QASource> findAllSources(Context context, long offset, int pageSize) {
return Arrays.stream(getSupportedSources())
.map((sourceName) -> findSource(sourceName))
.map((sourceName) -> findSource(context, sourceName))
.filter(Objects::nonNull)
.sorted(comparing(QASource::getTotalEvents).reversed())
.skip(offset)
.limit(pageSize)
@@ -500,14 +575,19 @@ public class QAEventServiceImpl implements QAEventService {
}
@Override
public long countSources() {
return getSupportedSources().length;
public long countSources(Context context) {
return Arrays.stream(getSupportedSources())
.map((sourceName) -> findSource(context, sourceName))
.filter(Objects::nonNull)
.filter(source -> source.getTotalEvents() > 0)
.count();
}
@Override
public List<QASource> findAllSourcesByTarget(UUID target, long offset, int pageSize) {
public List<QASource> findAllSourcesByTarget(Context context, UUID target, long offset, int pageSize) {
return Arrays.stream(getSupportedSources())
.map((sourceName) -> findSource(sourceName, target))
.map((sourceName) -> findSource(context, sourceName, target))
.filter(Objects::nonNull)
.sorted(comparing(QASource::getTotalEvents).reversed())
.filter(source -> source.getTotalEvents() > 0)
.skip(offset)
@@ -516,8 +596,12 @@ public class QAEventServiceImpl implements QAEventService {
}
@Override
public long countSourcesByTarget(UUID target) {
return getSupportedSources().length;
public long countSourcesByTarget(Context context, UUID target) {
return Arrays.stream(getSupportedSources())
.map((sourceName) -> findSource(context, sourceName, target))
.filter(Objects::nonNull)
.filter(source -> source.getTotalEvents() > 0)
.count();
}
@Override

View File

@@ -52,13 +52,13 @@ import org.dspace.qaevent.QATopic;
import org.dspace.qaevent.service.BrokerClientFactory;
import org.dspace.qaevent.service.QAEventService;
import org.dspace.qaevent.service.impl.BrokerClientFactoryImpl;
import org.dspace.services.ConfigurationService;
import org.dspace.utils.DSpace;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
/**
* Integration tests for {@link OpenaireEventsImport}.
*
@@ -77,11 +77,17 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
private BrokerClient mockBrokerClient = mock(BrokerClient.class);
private ConfigurationService configurationService = new DSpace().getConfigurationService();
@Before
public void setup() {
context.turnOffAuthorisationSystem();
// we want all the test in this class to be run using the administrator user as OpenAIRE events are only visible
// to administrators.
// Please note that test related to forbidden and unauthorized access to qaevent are included in
// the QAEventRestRepositoryIT here we are only testing that the import script is creating the expected events
context.setCurrentUser(admin);
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
@@ -91,7 +97,8 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
.build();
context.restoreAuthSystemState();
configurationService.setProperty("qaevent.sources", new String[]
{ QAEvent.OPENAIRE_SOURCE });
((BrokerClientFactoryImpl) BrokerClientFactory.getInstance()).setBrokerClient(mockBrokerClient);
}
@@ -161,11 +168,11 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
"Trying to read the QA events from the provided file",
"Found 5 events in the given file"));
assertThat(qaEventService.findAllSources(0, 20),
assertThat(qaEventService.findAllSources(context, 0, 20),
hasItem(QASourceMatcher.with(OPENAIRE_SOURCE, 5L))
);
List<QATopic> topicList = qaEventService.findAllTopicsBySource(OPENAIRE_SOURCE, 0, 20);
List<QATopic> topicList = qaEventService.findAllTopicsBySource(context, OPENAIRE_SOURCE, 0, 20);
assertThat(topicList, hasItem(QATopicMatcher.with("ENRICH/MORE/PROJECT", 1L)));
assertThat(topicList, hasItem(QATopicMatcher.with("ENRICH/MORE/PID", 1L)));
assertThat(topicList, hasItem(QATopicMatcher.with("ENRICH/MISSING/PID", 1L)));
@@ -178,7 +185,7 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
+ "\"projects[0].openaireId\":\"40|corda__h2020::6e32f5eb912688f2424c68b851483ea4\","
+ "\"projects[0].title\":\"Tracking Papyrus and Parchment Paths\"}";
assertThat(qaEventService.findEventsByTopicAndPage(OPENAIRE_SOURCE, "ENRICH/MORE/PROJECT", 0, 20),
assertThat(qaEventService.findEventsByTopicAndPage(context, OPENAIRE_SOURCE, "ENRICH/MORE/PROJECT", 0, 20),
contains(
pendingOpenaireEventWith("oai:www.openstarts.units.it:123456789/99998", firstItem,
"Egypt, crossroad of translations and literary interweavings", projectMessage,
@@ -186,7 +193,7 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
String abstractMessage = "{\"abstracts[0]\":\"Missing Abstract\"}";
assertThat(qaEventService.findEventsByTopicAndPage(OPENAIRE_SOURCE, "ENRICH/MISSING/ABSTRACT", 0, 20),
assertThat(qaEventService.findEventsByTopicAndPage(context, OPENAIRE_SOURCE, "ENRICH/MISSING/ABSTRACT", 0, 20),
contains(
pendingOpenaireEventWith("oai:www.openstarts.units.it:123456789/99999", secondItem, "Test Publication",
abstractMessage, "ENRICH/MISSING/ABSTRACT", 1.00d)));
@@ -221,16 +228,16 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
"Trying to read the QA events from the provided file",
"Found 5 events in the given file"));
assertThat(qaEventService.findAllSources(0, 20), hasItem(QASourceMatcher.with(OPENAIRE_SOURCE, 3L)));
assertThat(qaEventService.findAllSources(context, 0, 20), hasItem(QASourceMatcher.with(OPENAIRE_SOURCE, 3L)));
List<QATopic> topicList = qaEventService.findAllTopicsBySource(OPENAIRE_SOURCE, 0, 20);
List<QATopic> topicList = qaEventService.findAllTopicsBySource(context, OPENAIRE_SOURCE, 0, 20);
assertThat(topicList, hasItem(QATopicMatcher.with("ENRICH/MISSING/ABSTRACT", 1L)));
assertThat(topicList, hasItem(QATopicMatcher.with("ENRICH/MISSING/PROJECT", 1L)));
assertThat(topicList, hasItem(QATopicMatcher.with("ENRICH/MORE/PID", 1L)));
String abstractMessage = "{\"abstracts[0]\":\"Missing Abstract\"}";
assertThat(qaEventService.findEventsByTopicAndPage(OPENAIRE_SOURCE, "ENRICH/MISSING/ABSTRACT", 0, 20),
assertThat(qaEventService.findEventsByTopicAndPage(context, OPENAIRE_SOURCE, "ENRICH/MISSING/ABSTRACT", 0, 20),
contains(
pendingOpenaireEventWith("oai:www.openstarts.units.it:123456789/99999", item, "Test Publication",
abstractMessage, "ENRICH/MISSING/ABSTRACT", 1.00d)));
@@ -261,14 +268,14 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
"Trying to read the QA events from the provided file",
"Found 2 events in the given file"));
assertThat(qaEventService.findAllSources(0, 20), hasItem(QASourceMatcher.with(OPENAIRE_SOURCE, 1L)));
assertThat(qaEventService.findAllSources(context, 0, 20), hasItem(QASourceMatcher.with(OPENAIRE_SOURCE, 1L)));
assertThat(qaEventService.findAllTopicsBySource(OPENAIRE_SOURCE, 0, 20),
assertThat(qaEventService.findAllTopicsBySource(context, OPENAIRE_SOURCE, 0, 20),
contains(QATopicMatcher.with("ENRICH/MISSING/ABSTRACT", 1L)));
String abstractMessage = "{\"abstracts[0]\":\"Missing Abstract\"}";
assertThat(qaEventService.findEventsByTopicAndPage(OPENAIRE_SOURCE, "ENRICH/MISSING/ABSTRACT", 0, 20),
assertThat(qaEventService.findEventsByTopicAndPage(context, OPENAIRE_SOURCE, "ENRICH/MISSING/ABSTRACT", 0, 20),
contains(
pendingOpenaireEventWith("oai:www.openstarts.units.it:123456789/999991", secondItem, "Test Publication 2",
abstractMessage, "ENRICH/MISSING/ABSTRACT", 1.00d)));
@@ -290,9 +297,9 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
assertThat(handler.getWarningMessages(),empty());
assertThat(handler.getInfoMessages(), contains("Trying to read the QA events from the provided file"));
assertThat(qaEventService.findAllSources(0, 20), hasItem(QASourceMatcher.with(OPENAIRE_SOURCE, 0L)));
assertThat(qaEventService.findAllSources(context, 0, 20), hasItem(QASourceMatcher.with(OPENAIRE_SOURCE, 0L)));
assertThat(qaEventService.findAllTopicsBySource(OPENAIRE_SOURCE, 0, 20), empty());
assertThat(qaEventService.findAllTopicsBySource(context, OPENAIRE_SOURCE, 0, 20), empty());
verifyNoInteractions(mockBrokerClient);
}
@@ -337,9 +344,9 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
"Found 0 events from the subscription sub2",
"Found 2 events from the subscription sub3"));
assertThat(qaEventService.findAllSources(0, 20), hasItem(QASourceMatcher.with(OPENAIRE_SOURCE, 6L)));
assertThat(qaEventService.findAllSources(context, 0, 20), hasItem(QASourceMatcher.with(OPENAIRE_SOURCE, 6L)));
List<QATopic> topicList = qaEventService.findAllTopicsBySource(OPENAIRE_SOURCE, 0, 20);
List<QATopic> topicList = qaEventService.findAllTopicsBySource(context, OPENAIRE_SOURCE, 0, 20);
assertThat(topicList, hasItem(QATopicMatcher.with("ENRICH/MORE/PROJECT", 1L)));
assertThat(topicList, hasItem(QATopicMatcher.with("ENRICH/MORE/PID", 1L)));
assertThat(topicList, hasItem(QATopicMatcher.with("ENRICH/MISSING/PID", 1L)));
@@ -352,7 +359,7 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
+ "\"projects[0].openaireId\":\"40|corda__h2020::6e32f5eb912688f2424c68b851483ea4\","
+ "\"projects[0].title\":\"Tracking Papyrus and Parchment Paths\"}";
assertThat(qaEventService.findEventsByTopicAndPage(OPENAIRE_SOURCE, "ENRICH/MORE/PROJECT", 0, 20),
assertThat(qaEventService.findEventsByTopicAndPage(context, OPENAIRE_SOURCE, "ENRICH/MORE/PROJECT", 0, 20),
contains(
pendingOpenaireEventWith("oai:www.openstarts.units.it:123456789/99998", firstItem,
"Egypt, crossroad of translations and literary interweavings", projectMessage,
@@ -360,8 +367,8 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
String abstractMessage = "{\"abstracts[0]\":\"Missing Abstract\"}";
List<QAEvent> eventList = qaEventService.findEventsByTopicAndPage(OPENAIRE_SOURCE, "ENRICH/MISSING/ABSTRACT", 0,
20);
List<QAEvent> eventList = qaEventService.findEventsByTopicAndPage(context, OPENAIRE_SOURCE,
"ENRICH/MISSING/ABSTRACT", 0, 20);
assertThat(eventList, hasItem(
pendingOpenaireEventWith("oai:www.openstarts.units.it:123456789/99999", secondItem, "Test Publication",
abstractMessage, "ENRICH/MISSING/ABSTRACT", 1.00d)));
@@ -395,9 +402,9 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
assertThat(handler.getWarningMessages(), empty());
assertThat(handler.getInfoMessages(), contains("Trying to read the QA events from the OPENAIRE broker"));
assertThat(qaEventService.findAllSources(0, 20), hasItem(QASourceMatcher.with(OPENAIRE_SOURCE, 0L)));
assertThat(qaEventService.findAllSources(context, 0, 20), hasItem(QASourceMatcher.with(OPENAIRE_SOURCE, 0L)));
assertThat(qaEventService.findAllTopicsBySource(OPENAIRE_SOURCE, 0, 20), empty());
assertThat(qaEventService.findAllTopicsBySource(context, OPENAIRE_SOURCE, 0, 20), empty());
verify(mockBrokerClient).listSubscriptions(openaireURL, "user@test.com");
@@ -446,17 +453,18 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
"Found 0 events from the subscription sub2",
"Found 2 events from the subscription sub3"));
assertThat(qaEventService.findAllSources(0, 20), hasItem(QASourceMatcher.with(OPENAIRE_SOURCE, 6L)));
assertThat(qaEventService.findAllSources(context, 0, 20), hasItem(QASourceMatcher.with(OPENAIRE_SOURCE, 6L)));
List<QATopic> topicList = qaEventService.findAllTopicsBySource(OPENAIRE_SOURCE, 0, 20);
List<QATopic> topicList = qaEventService.findAllTopicsBySource(context, OPENAIRE_SOURCE, 0, 20);
assertThat(topicList, hasItem(QATopicMatcher.with("ENRICH/MORE/PROJECT", 1L)));
assertThat(topicList, hasItem(QATopicMatcher.with("ENRICH/MISSING/PID", 1L)));
assertThat(topicList, hasItem(QATopicMatcher.with("ENRICH/MORE/PID", 1L)));
assertThat(topicList, hasItem(QATopicMatcher.with("ENRICH/MISSING/PROJECT", 1L)));
assertThat(topicList, hasItem(QATopicMatcher.with("ENRICH/MISSING/ABSTRACT", 2L)));
assertThat(qaEventService.findEventsByTopicAndPage(OPENAIRE_SOURCE, "ENRICH/MORE/PROJECT", 0, 20), hasSize(1));
assertThat(qaEventService.findEventsByTopicAndPage(OPENAIRE_SOURCE, "ENRICH/MISSING/ABSTRACT", 0, 20),
assertThat(qaEventService.findEventsByTopicAndPage(context, OPENAIRE_SOURCE, "ENRICH/MORE/PROJECT", 0, 20),
hasSize(1));
assertThat(qaEventService.findEventsByTopicAndPage(context, OPENAIRE_SOURCE, "ENRICH/MISSING/ABSTRACT", 0, 20),
hasSize(2));
verify(mockBrokerClient).listSubscriptions(openaireURL, "user@test.com");
@@ -487,10 +495,11 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
String[] args = new String[] { "import-openaire-events", "-f", getFileLocation("event-more-review.json") };
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), handler, kernelImpl);
assertThat(qaEventService.findAllTopicsBySource(OPENAIRE_SOURCE, 0, 20), contains(
assertThat(qaEventService.findAllTopicsBySource(context, COAR_NOTIFY_SOURCE, 0, 20), contains(
QATopicMatcher.with("ENRICH/MORE/REVIEW", 1L)));
assertThat(qaEventService.findAllSources(0, 20), hasItem(QASourceMatcher.with(COAR_NOTIFY_SOURCE, 1L)));
assertThat(qaEventService.findAllSources(context, 0, 20),
hasItem(QASourceMatcher.with(COAR_NOTIFY_SOURCE, 1L)));
verifyNoInteractions(mockBrokerClient);
}

View File

@@ -79,7 +79,7 @@ public class QAEventRelatedRestController {
Context context = ContextUtil.obtainCurrentRequestContext();
QAEvent qaevent = qaEventService.findEventByEventId(qaeventId);
QAEvent qaevent = qaEventService.findEventByEventId(context, qaeventId);
if (qaevent == null) {
throw new ResourceNotFoundException("No such qa event: " + qaeventId);
}
@@ -122,7 +122,7 @@ public class QAEventRelatedRestController {
throws SQLException, AuthorizeException, IOException {
Context context = ContextUtil.obtainCurrentRequestContext();
QAEvent qaevent = qaEventService.findEventByEventId(qaeventId);
QAEvent qaevent = qaEventService.findEventByEventId(context, qaeventId);
if (qaevent == null) {
throw new ResourceNotFoundException("No such qa event: " + qaeventId);

View File

@@ -60,6 +60,7 @@ public class QAEventConverter implements DSpaceConverter<QAEvent, QAEventRest> {
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
rest.setSource(modelObject.getSource());
rest.setOriginalId(modelObject.getOriginalId());
rest.setProjection(projection);
rest.setTitle(modelObject.getTitle());

View File

@@ -51,11 +51,11 @@ public class QAEventRelatedLinkRepository extends AbstractDSpaceRestRepository i
* @param projection the projection object
* @return the item rest representation of the secondary item related to qa event
*/
@PreAuthorize("hasAuthority('ADMIN')")
@PreAuthorize("hasPermission(#id, 'QUALITYASSURANCEEVENT', 'READ')")
public ItemRest getRelated(@Nullable HttpServletRequest request, String id, @Nullable Pageable pageable,
Projection projection) {
Context context = obtainContext();
QAEvent qaEvent = qaEventService.findEventByEventId(id);
QAEvent qaEvent = qaEventService.findEventByEventId(context, id);
if (qaEvent == null) {
throw new ResourceNotFoundException("No qa event with ID: " + id);
}

View File

@@ -57,9 +57,9 @@ public class QAEventRestRepository extends DSpaceRestRepository<QAEventRest, Str
private ResourcePatch<QAEvent> resourcePatch;
@Override
@PreAuthorize("hasAuthority('ADMIN')")
@PreAuthorize("hasPermission(#id, 'QUALITYASSURANCEEVENT', 'READ')")
public QAEventRest findOne(Context context, String id) {
QAEvent qaEvent = qaEventService.findEventByEventId(id);
QAEvent qaEvent = qaEventService.findEventByEventId(context, id);
if (qaEvent == null) {
// check if this request is part of a patch flow
qaEvent = (QAEvent) requestService.getCurrentRequest().getAttribute("patchedNotificationEvent");
@@ -73,9 +73,10 @@ public class QAEventRestRepository extends DSpaceRestRepository<QAEventRest, Str
}
@SearchRestMethod(name = "findByTopic")
@PreAuthorize("hasAuthority('ADMIN')")
public Page<QAEventRest> findByTopic(Context context, @Parameter(value = "topic", required = true) String topic,
@PreAuthorize("hasAuthority('AUTHENTICATED')")
public Page<QAEventRest> findByTopic(@Parameter(value = "topic", required = true) String topic,
Pageable pageable) {
Context context = obtainContext();
String[] topicIdSplitted = topic.split(":", 3);
if (topicIdSplitted.length < 2) {
return null;
@@ -85,9 +86,9 @@ public class QAEventRestRepository extends DSpaceRestRepository<QAEventRest, Str
UUID target = topicIdSplitted.length == 3 ? UUID.fromString(topicIdSplitted[2]) : null;
List<QAEvent> qaEvents = null;
long count = 0L;
qaEvents = qaEventService.findEventsByTopicAndPageAndTarget(sourceName, topicName,
qaEvents = qaEventService.findEventsByTopicAndPageAndTarget(context, sourceName, topicName,
pageable.getOffset(), pageable.getPageSize(), target);
count = qaEventService.countEventsByTopicAndTarget(sourceName, topicName, target);
count = qaEventService.countEventsByTopicAndTarget(context, sourceName, topicName, target);
if (qaEvents == null) {
return null;
}
@@ -95,6 +96,7 @@ public class QAEventRestRepository extends DSpaceRestRepository<QAEventRest, Str
}
@Override
@PreAuthorize("hasPermission(#id, 'QUALITYASSURANCEEVENT', 'DELETE')")
protected void delete(Context context, String eventId) throws AuthorizeException {
Item item = findTargetItem(context, eventId);
EPerson eperson = context.getCurrentUser();
@@ -108,15 +110,15 @@ public class QAEventRestRepository extends DSpaceRestRepository<QAEventRest, Str
}
@Override
@PreAuthorize("hasAuthority('ADMIN')")
@PreAuthorize("hasPermission(#id, 'QUALITYASSURANCEEVENT', 'WRITE')")
protected void patch(Context context, HttpServletRequest request, String apiCategory, String model,
String id, Patch patch) throws SQLException, AuthorizeException {
QAEvent qaEvent = qaEventService.findEventByEventId(id);
QAEvent qaEvent = qaEventService.findEventByEventId(context, id);
resourcePatch.patch(context, qaEvent, patch.getOperations());
}
private Item findTargetItem(Context context, String eventId) {
QAEvent qaEvent = qaEventService.findEventByEventId(eventId);
QAEvent qaEvent = qaEventService.findEventByEventId(context, eventId);
if (qaEvent == null) {
return null;
}

View File

@@ -50,11 +50,11 @@ public class QAEventTargetLinkRepository extends AbstractDSpaceRestRepository im
* @param projection the projection object
* @return the item rest representation of the qa event target
*/
@PreAuthorize("hasAuthority('ADMIN')")
@PreAuthorize("hasPermission(#id, 'QUALITYASSURANCEEVENT', 'READ')")
public ItemRest getTarget(@Nullable HttpServletRequest request, String id, @Nullable Pageable pageable,
Projection projection) {
Context context = obtainContext();
QAEvent qaEvent = qaEventService.findEventByEventId(id);
QAEvent qaEvent = qaEventService.findEventByEventId(context, id);
if (qaEvent == null) {
throw new ResourceNotFoundException("No qa event with ID: " + id);
}

View File

@@ -44,18 +44,18 @@ public class QAEventTopicLinkRepository extends AbstractDSpaceRestRepository imp
* @param projection the projection object
* @return the qa topic rest representation
*/
@PreAuthorize("hasAuthority('ADMIN')")
@PreAuthorize("hasPermission(#id, 'QUALITYASSURANCEEVENT', 'READ')")
public QATopicRest getTopic(@Nullable HttpServletRequest request, String id, @Nullable Pageable pageable,
Projection projection) {
Context context = obtainContext();
QAEvent qaEvent = qaEventService.findEventByEventId(id);
QAEvent qaEvent = qaEventService.findEventByEventId(context, id);
if (qaEvent == null) {
throw new ResourceNotFoundException("No qa event with ID: " + id);
}
String source = qaEvent.getSource();
String topicName = qaEvent.getTopic();
QATopic topic = qaEventService
.findTopicBySourceAndNameAndTarget(source, topicName, null);
.findTopicBySourceAndNameAndTarget(context, source, topicName, null);
if (topic == null) {
throw new ResourceNotFoundException("No topic found with source: " + source + " topic: " + topicName);
}

View File

@@ -35,9 +35,9 @@ public class QASourceRestRepository extends DSpaceRestRepository<QASourceRest, S
private QAEventService qaEventService;
@Override
@PreAuthorize("hasAuthority('ADMIN')")
@PreAuthorize("hasPermission(#id, 'QUALITYASSURANCESOURCE', 'READ')")
public QASourceRest findOne(Context context, String id) {
QASource qaSource = qaEventService.findSource(id);
QASource qaSource = qaEventService.findSource(context, id);
if (qaSource == null) {
return null;
}
@@ -45,21 +45,21 @@ public class QASourceRestRepository extends DSpaceRestRepository<QASourceRest, S
}
@Override
@PreAuthorize("hasAuthority('ADMIN')")
@PreAuthorize("hasAuthority('AUTHENTICATED')")
public Page<QASourceRest> findAll(Context context, Pageable pageable) {
List<QASource> qaSources = qaEventService.findAllSources(pageable.getOffset(), pageable.getPageSize());
long count = qaEventService.countSources();
List<QASource> qaSources = qaEventService.findAllSources(context, pageable.getOffset(), pageable.getPageSize());
long count = qaEventService.countSources(context);
return converter.toRestPage(qaSources, pageable, count, utils.obtainProjection());
}
@SearchRestMethod(name = "byTarget")
@PreAuthorize("hasAuthority('ADMIN')")
public Page<QASourceRest> findByTarget(Context context,
@Parameter(value = "target", required = true) UUID target,
@PreAuthorize("hasAuthority('AUTHENTICATED')")
public Page<QASourceRest> findByTarget(@Parameter(value = "target", required = true) UUID target,
Pageable pageable) {
Context context = obtainContext();
List<QASource> topics = qaEventService
.findAllSourcesByTarget(target, pageable.getOffset(), pageable.getPageSize());
long count = qaEventService.countSourcesByTarget(target);
.findAllSourcesByTarget(context, target, pageable.getOffset(), pageable.getPageSize());
long count = qaEventService.countSourcesByTarget(context, target);
if (topics == null) {
return null;
}

View File

@@ -40,7 +40,7 @@ public class QATopicRestRepository extends DSpaceRestRepository<QATopicRest, Str
private static final Logger log = LogManager.getLogger();
@Override
@PreAuthorize("hasAuthority('ADMIN')")
@PreAuthorize("hasPermission(#id, 'QUALITYASSURANCETOPIC', 'READ')")
public QATopicRest findOne(Context context, String id) {
String[] topicIdSplitted = id.split(":", 3);
if (topicIdSplitted.length < 2) {
@@ -49,7 +49,7 @@ public class QATopicRestRepository extends DSpaceRestRepository<QATopicRest, Str
String sourceName = topicIdSplitted[0];
String topicName = topicIdSplitted[1].replaceAll("!", "/");
UUID target = topicIdSplitted.length == 3 ? UUID.fromString(topicIdSplitted[2]) : null;
QATopic topic = qaEventService.findTopicBySourceAndNameAndTarget(sourceName, topicName, target);
QATopic topic = qaEventService.findTopicBySourceAndNameAndTarget(context, sourceName, topicName, target);
if (topic == null) {
return null;
}
@@ -62,12 +62,13 @@ public class QATopicRestRepository extends DSpaceRestRepository<QATopicRest, Str
}
@SearchRestMethod(name = "bySource")
@PreAuthorize("hasAuthority('ADMIN')")
public Page<QATopicRest> findBySource(Context context,
@Parameter(value = "source", required = true) String source, Pageable pageable) {
List<QATopic> topics = qaEventService.findAllTopicsBySource(source,
@PreAuthorize("hasAuthority('AUTHENTICATED')")
public Page<QATopicRest> findBySource(@Parameter(value = "source", required = true) String source,
Pageable pageable) {
Context context = obtainContext();
List<QATopic> topics = qaEventService.findAllTopicsBySource(context, source,
pageable.getOffset(), pageable.getPageSize());
long count = qaEventService.countTopicsBySource(source);
long count = qaEventService.countTopicsBySource(context, source);
if (topics == null) {
return null;
}
@@ -75,14 +76,14 @@ public class QATopicRestRepository extends DSpaceRestRepository<QATopicRest, Str
}
@SearchRestMethod(name = "byTarget")
@PreAuthorize("hasAuthority('ADMIN')")
public Page<QATopicRest> findByTarget(Context context,
@Parameter(value = "target", required = true) UUID target,
@PreAuthorize("hasAuthority('AUTHENTICATED')")
public Page<QATopicRest> findByTarget(@Parameter(value = "target", required = true) UUID target,
@Parameter(value = "source", required = true) String source,
Pageable pageable) {
Context context = obtainContext();
List<QATopic> topics = qaEventService
.findAllTopicsBySourceAndTarget(source, target, pageable.getOffset(), pageable.getPageSize());
long count = qaEventService.countTopicsBySourceAndTarget(source, target);
.findAllTopicsBySourceAndTarget(context, source, target, pageable.getOffset(), pageable.getPageSize());
long count = qaEventService.countTopicsBySourceAndTarget(context, source, target);
if (topics == null) {
return null;
}

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.security;
import java.io.Serializable;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.dspace.app.rest.model.QAEventRest;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.content.QAEvent;
import org.dspace.core.Context;
import org.dspace.qaevent.service.QAEventSecurityService;
import org.dspace.qaevent.service.QAEventService;
import org.dspace.services.RequestService;
import org.dspace.services.model.Request;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
/**
* This class will handle Permissions for the {@link QAEventRest} object and its calls
*
* @author Andrea Bollini (4Science)
*/
@Component
public class QAEventRestPermissionEvaluatorPlugin extends RestObjectPermissionEvaluatorPlugin {
private static final Logger log = LoggerFactory.getLogger(QAEventRestPermissionEvaluatorPlugin.class);
@Autowired
private QAEventService qaEventService;
@Autowired
private QAEventSecurityService qaEventSecurityService;
@Autowired
private RequestService requestService;
/**
* Responsible for checking whether or not the user has access to the requested QASource
*
* @param targetType the type of Rest Object that should be checked for permission. This class would deal only with
* qaevent
* @param targetId string to extract the sourcename from
*/
@Override
public boolean hasDSpacePermission(Authentication authentication, Serializable targetId, String targetType,
DSpaceRestPermission restPermission) {
if (StringUtils.equalsIgnoreCase(QAEventRest.NAME, targetType)) {
log.debug("Checking permission for targetId {}", targetId);
Request request = requestService.getCurrentRequest();
Context context = ContextUtil.obtainContext(request.getHttpServletRequest());
if (Objects.isNull(targetId)) {
return true;
}
QAEvent qaEvent = qaEventService.findEventByEventId(context, targetId.toString());
// everyone is expected to be able to see a not existing event (so we can return not found)
if ((qaEvent == null
|| qaEventSecurityService.canSeeEvent(context, context.getCurrentUser(), qaEvent))) {
return true;
}
}
return false;
}
}

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.app.rest.security;
import java.io.Serializable;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.dspace.app.rest.model.QASourceRest;
import org.dspace.app.rest.model.QATopicRest;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.core.Context;
import org.dspace.qaevent.QATopic;
import org.dspace.qaevent.service.QAEventSecurityService;
import org.dspace.services.RequestService;
import org.dspace.services.model.Request;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
/**
* This class will handle Permissions for the {@link QASourceRest} object and {@link QATopic}
*
* @author Andrea Bollini (4Science)
*/
@Component
public class QASourceRestPermissionEvaluatorPlugin extends RestObjectPermissionEvaluatorPlugin {
private static final Logger log = LoggerFactory.getLogger(QASourceRestPermissionEvaluatorPlugin.class);
@Autowired
private QAEventSecurityService qaEventSecurityService;
@Autowired
private RequestService requestService;
/**
* Responsible for checking whether or not the user has access to the requested QASource
*
* @param targetType the type of Rest Object that should be checked for permission. This class would deal only with
* qasource and qatopic
* @param targetId string to extract the sourcename from
*/
@Override
public boolean hasDSpacePermission(Authentication authentication, Serializable targetId, String targetType,
DSpaceRestPermission restPermission) {
if (StringUtils.equalsIgnoreCase(QASourceRest.NAME, targetType)
|| StringUtils.equalsIgnoreCase(QATopicRest.NAME, targetType)) {
log.debug("Checking permission for targetId {}", targetId);
Request request = requestService.getCurrentRequest();
Context context = ContextUtil.obtainContext(request.getHttpServletRequest());
if (Objects.isNull(targetId)) {
return true;
}
// the source name is always the first part of the id both for a source than a topic
// users can see all the topic in source that they can access, eventually they will have no
// events visible to them
String sourceName = targetId.toString().split(":")[0];
return qaEventSecurityService.canSeeSource(context, context.getCurrentUser(), sourceName);
}
return false;
}
}

View File

@@ -118,9 +118,10 @@ public class LDNInboxControllerIT extends AbstractControllerIntegrationTest {
ldnMessageService.extractAndProcessMessageFromQueue(context);
assertThat(qaEventService.findAllSources(0, 20), hasItem(QASourceMatcher.with(COAR_NOTIFY_SOURCE, 1L)));
assertThat(qaEventService.findAllSources(context, 0, 20),
hasItem(QASourceMatcher.with(COAR_NOTIFY_SOURCE, 1L)));
assertThat(qaEventService.findAllTopicsBySource(COAR_NOTIFY_SOURCE, 0, 20), hasItem(
assertThat(qaEventService.findAllTopicsBySource(context, COAR_NOTIFY_SOURCE, 0, 20), hasItem(
QATopicMatcher.with("ENRICH/MORE/REVIEW", 1L)));
}

View File

@@ -10,6 +10,7 @@ package org.dspace.app.rest;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasNoJsonPath;
import static org.dspace.content.QAEvent.COAR_NOTIFY_SOURCE;
import static org.dspace.content.QAEvent.OPENAIRE_SOURCE;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasSize;
@@ -40,6 +41,7 @@ import org.dspace.app.rest.repository.QAEventRestRepository;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.EPersonBuilder;
import org.dspace.builder.EntityTypeBuilder;
import org.dspace.builder.ItemBuilder;
import org.dspace.builder.NotifyServiceBuilder;
@@ -51,6 +53,7 @@ import org.dspace.content.Item;
import org.dspace.content.QAEvent;
import org.dspace.content.QAEventProcessed;
import org.dspace.content.service.ItemService;
import org.dspace.eperson.EPerson;
import org.dspace.qaevent.action.ASimpleMetadataAction;
import org.dspace.qaevent.dao.QAEventsDao;
import org.hamcrest.Matchers;
@@ -96,17 +99,32 @@ public class QAEventRestRepositoryIT extends AbstractControllerIntegrationTest {
QAEvent event1 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
.withTopic("ENRICH/MISSING/PID")
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
QAEvent event4 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
QAEvent event2 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
.withTopic("ENRICH/MISSING/ABSTRACT")
.withMessage("{\"abstracts[0]\": \"Descrizione delle caratteristiche...\"}").build();
EPerson anotherSubmitter = EPersonBuilder.createEPerson(context).withEmail("another-submitter@example.com")
.withPassword(password).build();
context.setCurrentUser(anotherSubmitter);
QAEvent event3 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
.withSource(COAR_NOTIFY_SOURCE)
.withTopic("ENRICH/MORE/REVIEW")
.withMessage("{\"abstracts[0]\": \"Descrizione delle caratteristiche...\"}").build();
context.restoreAuthSystemState();
String authToken = getAuthToken(admin.getEmail(), password);
getClient(authToken).perform(get("/api/integration/qualityassuranceevents/" + event1.getEventId()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventEntry(event1)));
getClient(authToken).perform(get("/api/integration/qualityassuranceevents/" + event4.getEventId()))
getClient(authToken).perform(get("/api/integration/qualityassuranceevents/" + event2.getEventId()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventEntry(event4)));
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventEntry(event2)));
getClient(authToken).perform(get("/api/integration/qualityassuranceevents/" + event3.getEventId()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventEntry(event3)));
authToken = getAuthToken(anotherSubmitter.getEmail(), password);
// eperson should be see the coar-notify event related to the item that it has submitted
getClient(authToken).perform(get("/api/integration/qualityassuranceevents/" + event3.getEventId()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", QAEventMatcher.matchQAEventEntry(event3)));
}
@Test
@@ -164,10 +182,19 @@ public class QAEventRestRepositoryIT extends AbstractControllerIntegrationTest {
QAEvent event1 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
.withTopic("ENRICH/MISSING/PID")
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
EPerson anotherSubmitter = EPersonBuilder.createEPerson(context).withEmail("another_submitter@example.com")
.build();
context.setCurrentUser(anotherSubmitter);
QAEvent event2 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
.withSource(COAR_NOTIFY_SOURCE)
.withTopic("ENRICH/MORE/REVIEW")
.withMessage("{\"href\":\"https://doi.org/10.2307/2144300\"}").build();
context.restoreAuthSystemState();
String authToken = getAuthToken(eperson.getEmail(), password);
getClient(authToken).perform(get("/api/integration/qualityassuranceevents/" + event1.getEventId()))
.andExpect(status().isForbidden());
getClient(authToken).perform(get("/api/integration/qualityassuranceevents/" + event2.getEventId()))
.andExpect(status().isForbidden());
}
@Test
@@ -178,10 +205,22 @@ public class QAEventRestRepositoryIT extends AbstractControllerIntegrationTest {
String uuid = UUID.randomUUID().toString();
Item item = ItemBuilder.createItem(context, col1).withTitle("Tracking Papyrus and Parchment Paths")
.build();
QAEventBuilder qBuilder = QAEventBuilder.createTarget(context, item)
QAEvent event1 = QAEventBuilder.createTarget(context, item)
.withTopic("ENRICH/MISSING/PID")
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}");
QAEvent event1 = qBuilder.build();
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}")
.build();
QAEvent event2 = QAEventBuilder.createTarget(context, item)
.withSource(COAR_NOTIFY_SOURCE)
.withTopic("ENRICH/MORE/REVIEW")
.withMessage("{\"href\":\"https://doi.org/10.2307/2144301\"}").build();
EPerson anotherSubmitter = EPersonBuilder.createEPerson(context).withEmail("another-submitter@example.com")
.withPassword(password).build();
context.setCurrentUser(anotherSubmitter);
// this event is related to a new item not submitted by eperson
QAEvent event3 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
.withSource(COAR_NOTIFY_SOURCE)
.withTopic("ENRICH/MORE/REVIEW")
.withMessage("{\"href\":\"https://doi.org/10.2307/2144300\"}").build();
context.restoreAuthSystemState();
String authToken = getAuthToken(admin.getEmail(), password);
getClient(authToken)
@@ -216,6 +255,15 @@ public class QAEventRestRepositoryIT extends AbstractControllerIntegrationTest {
.andExpect(jsonPath("$._embedded.qualityassuranceevents",
Matchers.contains(QAEventMatcher.matchQAEventEntry(event1))))
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(1)));
// use the coar-notify source that has a custom security
getClient(authToken)
.perform(
get("/api/integration/qualityassuranceevents/search/findByTopic")
.param("topic", QAEvent.COAR_NOTIFY_SOURCE + ":ENRICH!MORE!REVIEW:" + uuid.toString()))
.andExpect(status().isOk()).andExpect(jsonPath("$._embedded.qualityassuranceevents", Matchers.hasSize(1)))
.andExpect(jsonPath("$._embedded.qualityassuranceevents",
Matchers.contains(QAEventMatcher.matchQAEventEntry(event2))))
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(1)));
// check for an existing topic
getClient(authToken)
.perform(
@@ -225,48 +273,46 @@ public class QAEventRestRepositoryIT extends AbstractControllerIntegrationTest {
.andExpect(jsonPath("$._embedded.qualityassuranceevents",
Matchers.contains(QAEventMatcher.matchQAEventEntry(event1))))
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(1)));
}
@Test
public void findByTopicTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
QAEvent event1 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
.withTopic("ENRICH/MISSING/PID")
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
QAEvent event2 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 2")
.withTopic("ENRICH/MISSING/PID")
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}").build();
QAEvent event3 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 3")
.withTopic("ENRICH/MORE/PID")
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"10.2307/2144302\"}").build();
QAEvent event4 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
.withTopic("ENRICH/MISSING/ABSTRACT")
.withMessage("{\"abstracts[0]\": \"Descrizione delle caratteristiche...\"}").build();
context.restoreAuthSystemState();
String authToken = getAuthToken(admin.getEmail(), password);
// use the coar-notify source that has a custom security
getClient(authToken)
.perform(
get("/api/integration/qualityassuranceevents/search/findByTopic")
.param("topic", QAEvent.COAR_NOTIFY_SOURCE + ":ENRICH!MORE!REVIEW"))
.andExpect(status().isOk()).andExpect(jsonPath("$._embedded.qualityassuranceevents", Matchers.hasSize(2)))
.andExpect(jsonPath("$._embedded.qualityassuranceevents",
Matchers.containsInAnyOrder(
QAEventMatcher.matchQAEventEntry(event2),
QAEventMatcher.matchQAEventEntry(event3))))
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(2)));
// check results for eperson
authToken = getAuthToken(eperson.getEmail(), password);
// check for an item that was submitted by eperson but in a qasource restricted to admins
getClient(authToken)
.perform(
get("/api/integration/qualityassuranceevents/search/findByTopic")
.param("topic", QAEvent.OPENAIRE_SOURCE + ":ENRICH!MISSING!PID:" + uuid.toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(0)));
// use the coar-notify source that has a custom security, only 1 event is related to the item submitted by
// eperson
getClient(authToken)
.perform(
get("/api/integration/qualityassuranceevents/search/findByTopic")
.param("topic", QAEvent.COAR_NOTIFY_SOURCE + ":ENRICH!MORE!REVIEW:" + uuid.toString()))
.andExpect(status().isOk()).andExpect(jsonPath("$._embedded.qualityassuranceevents", Matchers.hasSize(1)))
.andExpect(jsonPath("$._embedded.qualityassuranceevents",
Matchers.contains(QAEventMatcher.matchQAEventEntry(event2))))
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(1)));
// check for an existing topic
getClient(authToken)
.perform(
get("/api/integration/qualityassuranceevents/search/findByTopic")
.param("topic", QAEvent.OPENAIRE_SOURCE + ":ENRICH!MISSING!PID"))
.andExpect(status().isOk()).andExpect(jsonPath("$._embedded.qualityassuranceevents", Matchers.hasSize(2)))
.andExpect(jsonPath("$._embedded.qualityassuranceevents",
Matchers.containsInAnyOrder(QAEventMatcher.matchQAEventEntry(event1),
QAEventMatcher.matchQAEventEntry(event2))))
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(2)));
getClient(authToken)
.perform(get("/api/integration/qualityassuranceevents/search/findByTopic")
.param("topic", QAEvent.OPENAIRE_SOURCE + ":ENRICH!MISSING!ABSTRACT"))
.andExpect(status().isOk()).andExpect(jsonPath("$._embedded.qualityassuranceevents", Matchers.hasSize(1)))
.andExpect(jsonPath("$._embedded.qualityassuranceevents",
Matchers.containsInAnyOrder(QAEventMatcher.matchQAEventEntry(event4))))
.andExpect(jsonPath("$.page.size", is(20))).andExpect(jsonPath("$.page.totalElements", is(1)));
getClient(authToken)
.perform(get("/api/integration/qualityassuranceevents/search/findByTopic")
.param("topic", QAEvent.OPENAIRE_SOURCE + ":not-existing"))
.andExpect(status().isOk()).andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(0)));
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(0)));
}
@Test
@@ -289,6 +335,33 @@ public class QAEventRestRepositoryIT extends AbstractControllerIntegrationTest {
QAEvent event5 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 5")
.withTopic("ENRICH/MISSING/PID")
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"2144304\"}").build();
QAEvent event6 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
.withSource(OPENAIRE_SOURCE)
.withTopic("ENRICH/MISSING/PID")
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
QAEvent event7 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 2")
.withSource(OPENAIRE_SOURCE)
.withTopic("ENRICH/MISSING/PID")
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}").build();
QAEvent event8 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 3")
.withSource(OPENAIRE_SOURCE)
.withTopic("ENRICH/MISSING/PID")
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144302\"}").build();
QAEvent event9 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
.withSource(OPENAIRE_SOURCE)
.withTopic("ENRICH/MISSING/PID")
.withMessage("{\"pids[0].type\":\"pmc\",\"pids[0].value\":\"2144303\"}").build();
QAEvent event10 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 5")
.withSource(OPENAIRE_SOURCE)
.withTopic("ENRICH/MISSING/PID")
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"2144304\"}").build();
context.setCurrentUser(admin);
// this event will be related to an item submitted by the admin
QAEvent event11 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 5")
.withSource(OPENAIRE_SOURCE)
.withTopic("ENRICH/MISSING/PID")
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"2144304\"}").build();
context.restoreAuthSystemState();
String authToken = getAuthToken(admin.getEmail(), password);
getClient(authToken)
@@ -316,7 +389,7 @@ public class QAEventRestRepositoryIT extends AbstractControllerIntegrationTest {
Matchers.allOf(
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
Matchers.containsString("topic=" + QAEvent.OPENAIRE_SOURCE + ":ENRICH!MISSING!PID"),
Matchers.containsString("page=2"),
Matchers.containsString("page=5"),
Matchers.containsString("size=2"))))
.andExpect(jsonPath("$._links.first.href",
Matchers.allOf(
@@ -326,8 +399,8 @@ public class QAEventRestRepositoryIT extends AbstractControllerIntegrationTest {
Matchers.containsString("size=2"))))
.andExpect(jsonPath("$._links.prev.href").doesNotExist())
.andExpect(jsonPath("$.page.size", is(2)))
.andExpect(jsonPath("$.page.totalPages", is(3)))
.andExpect(jsonPath("$.page.totalElements", is(5)));
.andExpect(jsonPath("$.page.totalPages", is(6)))
.andExpect(jsonPath("$.page.totalElements", is(11)));
getClient(authToken)
.perform(
@@ -355,7 +428,7 @@ public class QAEventRestRepositoryIT extends AbstractControllerIntegrationTest {
Matchers.allOf(
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
Matchers.containsString("topic=" + QAEvent.OPENAIRE_SOURCE + ":ENRICH!MISSING!PID"),
Matchers.containsString("page=2"),
Matchers.containsString("page=5"),
Matchers.containsString("size=2"))))
.andExpect(jsonPath("$._links.first.href",
Matchers.allOf(
@@ -370,30 +443,35 @@ public class QAEventRestRepositoryIT extends AbstractControllerIntegrationTest {
Matchers.containsString("page=0"),
Matchers.containsString("size=2"))))
.andExpect(jsonPath("$.page.size", is(2)))
.andExpect(jsonPath("$.page.totalPages", is(3)))
.andExpect(jsonPath("$.page.totalElements", is(5)));
.andExpect(jsonPath("$.page.totalPages", is(6)))
.andExpect(jsonPath("$.page.totalElements", is(11)));
getClient(authToken)
.perform(
get("/api/integration/qualityassuranceevents/search/findByTopic")
.param("topic", QAEvent.OPENAIRE_SOURCE + ":ENRICH!MISSING!PID")
.param("size", "2").param("page", "2"))
.andExpect(status().isOk()).andExpect(jsonPath("$._embedded.qualityassuranceevents", Matchers.hasSize(1)))
.andExpect(status().isOk()).andExpect(jsonPath("$._embedded.qualityassuranceevents", Matchers.hasSize(2)))
.andExpect(jsonPath("$._embedded.qualityassuranceevents",
Matchers.containsInAnyOrder(
Matchers.hasItem(
QAEventMatcher.matchQAEventEntry(event5))))
.andExpect(jsonPath("$._links.self.href",
Matchers.allOf(
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
Matchers.containsString("topic=" + QAEvent.OPENAIRE_SOURCE + ":ENRICH!MISSING!PID"),
Matchers.containsString("page=2"),
Matchers.containsString("size=2"))))
.andExpect(jsonPath("$._links.next.href").doesNotExist())
Matchers.allOf(
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
Matchers.containsString("topic=" + QAEvent.OPENAIRE_SOURCE + ":ENRICH!MISSING!PID"),
Matchers.containsString("page=2"),
Matchers.containsString("size=2"))))
.andExpect(jsonPath("$._links.next.href",
Matchers.allOf(
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
Matchers.containsString("topic=" + QAEvent.OPENAIRE_SOURCE + ":ENRICH!MISSING!PID"),
Matchers.containsString("page=3"),
Matchers.containsString("size=2"))))
.andExpect(jsonPath("$._links.last.href",
Matchers.allOf(
Matchers.containsString("/api/integration/qualityassuranceevents/search/findByTopic?"),
Matchers.containsString("topic=" + QAEvent.OPENAIRE_SOURCE + ":ENRICH!MISSING!PID"),
Matchers.containsString("page=2"),
Matchers.containsString("page=5"),
Matchers.containsString("size=2"))))
.andExpect(jsonPath("$._links.first.href",
Matchers.allOf(
@@ -408,9 +486,21 @@ public class QAEventRestRepositoryIT extends AbstractControllerIntegrationTest {
Matchers.containsString("page=1"),
Matchers.containsString("size=2"))))
.andExpect(jsonPath("$.page.size", is(2)))
.andExpect(jsonPath("$.page.totalPages", is(3)))
.andExpect(jsonPath("$.page.totalElements", is(5)));
.andExpect(jsonPath("$.page.totalPages", is(6)))
.andExpect(jsonPath("$.page.totalElements", is(11)));
// check if the pagination is working properly also when a security filter is in place
authToken = getAuthToken(eperson.getEmail(), password);
getClient(authToken)
.perform(
get("/api/integration/qualityassuranceevents/search/findByTopic")
.param("topic", QAEvent.OPENAIRE_SOURCE + ":ENRICH!MISSING!PID")
.param("size", "2"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasNoJsonPath("$._embedded.qualityassuranceevents")))
.andExpect(jsonPath("$.page.size", is(2)))
.andExpect(jsonPath("$.page.totalPages", is(0)))
.andExpect(jsonPath("$.page.totalElements", is(0)));
}
@Test
@@ -438,32 +528,6 @@ public class QAEventRestRepositoryIT extends AbstractControllerIntegrationTest {
.andExpect(status().isUnauthorized());
}
@Test
public void findByTopicForbiddenTest() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context).withName("Parent Community").build();
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build();
QAEvent event1 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
.withTopic("ENRICH/MISSING/PID")
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}").build();
QAEvent event2 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 2")
.withTopic("ENRICH/MISSING/PID")
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144301\"}").build();
QAEvent event3 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 3")
.withTopic("ENRICH/MORE/PID")
.withMessage("{\"pids[0].type\":\"pmid\",\"pids[0].value\":\"10.2307/2144302\"}").build();
QAEvent event4 = QAEventBuilder.createTarget(context, col1, "Science and Freedom 4")
.withTopic("ENRICH/MISSING/ABSTRACT")
.withMessage("{\"abstracts[0]\": \"Descrizione delle caratteristiche...\"}").build();
context.restoreAuthSystemState();
String epersonToken = getAuthToken(eperson.getEmail(), password);
getClient(epersonToken)
.perform(
get("/api/integration/qualityassuranceevents/search/findByTopic")
.param("topic", QAEvent.OPENAIRE_SOURCE + ":ENRICH!MISSING!PID"))
.andExpect(status().isForbidden());
}
@Test
public void findByTopicBadRequestTest() throws Exception {
context.turnOffAuthorisationSystem();

View File

@@ -15,6 +15,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
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.repository.QASourceRestRepository;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
@@ -62,7 +63,7 @@ public class QASourceRestRepositoryIT extends AbstractControllerIntegrationTest
context.restoreAuthSystemState();
configurationService.setProperty("qaevent.sources",
new String[] { "openaire", "test-source", "test-source-2" });
new String[] { "openaire", "coar-notify", "test-source", "test-source-2" });
}
@@ -74,10 +75,15 @@ public class QASourceRestRepositoryIT extends AbstractControllerIntegrationTest
createEvent("openaire", "TOPIC/OPENAIRE/1", "Title 1");
createEvent("openaire", "TOPIC/OPENAIRE/2", "Title 2");
createEvent("openaire", "TOPIC/OPENAIRE/2", "Title 3");
createEvent("openaire", "TOPIC/OPENAIRE/2", "Title 4");
createEvent("test-source", "TOPIC/TEST/1", "Title 4");
createEvent("test-source", "TOPIC/TEST/1", "Title 5");
createEvent("test-source", "TOPIC/TEST/1", "Title 6");
createEvent("coar-notify", "TOPIC", "Title 7");
context.setCurrentUser(eperson);
createEvent("coar-notify", "TOPIC", "Title 8");
createEvent("coar-notify", "TOPIC", "Title 9");
context.setCurrentUser(null);
context.restoreAuthSystemState();
String authToken = getAuthToken(admin.getEmail(), password);
@@ -85,27 +91,22 @@ public class QASourceRestRepositoryIT extends AbstractControllerIntegrationTest
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.qualityassurancesources", contains(
matchQASourceEntry("openaire", 3),
matchQASourceEntry("openaire", 4),
matchQASourceEntry("coar-notify", 3),
matchQASourceEntry("test-source", 2),
matchQASourceEntry("test-source-2", 0))))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(3)));
}
@Test
public void testFindAllForbidden() throws Exception {
context.turnOffAuthorisationSystem();
createEvent("openaire", "TOPIC/OPENAIRE/1", "Title 1");
createEvent("test-source", "TOPIC/TEST/1", "Title 4");
context.restoreAuthSystemState();
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(get("/api/integration/qualityassurancesources"))
.andExpect(status().isForbidden());
.andExpect(jsonPath("$.page.totalElements", is(4)));
getClient(authToken).perform(get("/api/integration/qualityassurancesources"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.qualityassurancesources", contains(
matchQASourceEntry("coar-notify", 2),
matchQASourceEntry("openaire", 0),
matchQASourceEntry("test-source", 0),
matchQASourceEntry("test-source-2", 0))))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(4)));
}
@@ -135,7 +136,11 @@ public class QASourceRestRepositoryIT extends AbstractControllerIntegrationTest
createEvent("test-source", "TOPIC/TEST/1", "Title 4");
createEvent("test-source", "TOPIC/TEST/1", "Title 5");
createEvent("coar-notify", "TOPIC", "Title 7");
context.setCurrentUser(eperson);
createEvent("coar-notify", "TOPIC", "Title 8");
createEvent("coar-notify", "TOPIC", "Title 9");
context.setCurrentUser(null);
context.restoreAuthSystemState();
String authToken = getAuthToken(admin.getEmail(), password);
@@ -144,6 +149,12 @@ public class QASourceRestRepositoryIT extends AbstractControllerIntegrationTest
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$", matchQASourceEntry("openaire", 3)));
getClient(authToken).perform(get("/api/integration/qualityassurancesources/coar-notify"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$", matchQASourceEntry("coar-notify", 3)));
getClient(authToken).perform(get("/api/integration/qualityassurancesources/test-source"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
@@ -157,6 +168,18 @@ public class QASourceRestRepositoryIT extends AbstractControllerIntegrationTest
getClient(authToken).perform(get("/api/integration/qualityassurancesources/unknown-test-source"))
.andExpect(status().isNotFound());
authToken = getAuthToken(eperson.getEmail(), password);
getClient(authToken).perform(get("/api/integration/qualityassurancesources/openaire"))
.andExpect(status().isForbidden());
getClient(authToken).perform(get("/api/integration/qualityassurancesources/unknown-test-source"))
.andExpect(status().isNotFound());
// the eperson will see only 2 events in coar-notify as 1 is related ot an item was submitted by other
getClient(authToken).perform(get("/api/integration/qualityassurancesources/coar-notify"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$", matchQASourceEntry("coar-notify", 2)));
}
@Test
@@ -203,6 +226,12 @@ public class QASourceRestRepositoryIT extends AbstractControllerIntegrationTest
createEvent("test-source", "TOPIC/TEST/1", target1);
createEvent("test-source", "TOPIC/TEST/1", target2);
context.setCurrentUser(eperson);
Item target3 = ItemBuilder.createItem(context, col).withTitle("Test item3").build();
context.setCurrentUser(null);
createEvent("coar-notify", "TOPIC", target3);
createEvent("coar-notify", "TOPIC", target3);
createEvent("coar-notify", "TOPIC", target2);
context.restoreAuthSystemState();
String authToken = getAuthToken(admin.getEmail(), password);
@@ -221,7 +250,40 @@ public class QASourceRestRepositoryIT extends AbstractControllerIntegrationTest
target2.getID().toString()))
.andExpect(status().isOk()).andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.qualityassurancesources",
contains(matchQASourceEntry("test-source:" + target2.getID().toString(), 1))))
contains(
matchQASourceEntry("coar-notify:" + target2.getID().toString(), 1),
matchQASourceEntry("test-source:" + target2.getID().toString(), 1))))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(2)));
getClient(authToken)
.perform(get("/api/integration/qualityassurancesources/search/byTarget").param("target",
target3.getID().toString()))
.andExpect(status().isOk()).andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.qualityassurancesources",
contains(matchQASourceEntry("coar-notify:" + target3.getID().toString(), 2))))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(1)));
// check with our eperson submitter
authToken = getAuthToken(eperson.getEmail(), password);
getClient(authToken)
.perform(get("/api/integration/qualityassurancesources/search/byTarget").param("target",
target1.getID().toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(0)));
getClient(authToken)
.perform(get("/api/integration/qualityassurancesources/search/byTarget").param("target",
target2.getID().toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(0)));
getClient(authToken)
.perform(get("/api/integration/qualityassurancesources/search/byTarget").param("target",
target3.getID().toString()))
.andExpect(status().isOk()).andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.qualityassurancesources",
contains(matchQASourceEntry("coar-notify:" + target3.getID().toString(), 2))))
.andExpect(jsonPath("$.page.size", is(20)))
.andExpect(jsonPath("$.page.totalElements", is(1)));
}
@@ -269,27 +331,6 @@ public class QASourceRestRepositoryIT extends AbstractControllerIntegrationTest
.andExpect(status().isUnauthorized());
}
@Test
public void testFindByTargetForbidden() throws Exception {
context.turnOffAuthorisationSystem();
Community com = CommunityBuilder.createCommunity(context).withName("Test community").build();
Collection col = CollectionBuilder.createCollection(context, com).withName("Test collection").build();
Item target1 = ItemBuilder.createItem(context, col).withTitle("Test item1").build();
Item target2 = ItemBuilder.createItem(context, col).withTitle("Test item2").build();
createEvent("openaire", "TOPIC/OPENAIRE/1", target1);
createEvent("openaire", "TOPIC/OPENAIRE/2", target1);
createEvent("test-source", "TOPIC/TEST/1", target1);
createEvent("test-source", "TOPIC/TEST/1", target2);
context.restoreAuthSystemState();
String authToken = getAuthToken(eperson.getEmail(), password);
getClient(authToken)
.perform(get("/api/integration/qualityassurancesources/search/byTarget").param("target",
target1.getID().toString()))
.andExpect(status().isForbidden());
}
private QAEvent createEvent(String source, String topic, String title) {
return QAEventBuilder.createTarget(context, target)
.withSource(source)

View File

@@ -30,7 +30,7 @@ public class SubmissionCOARNotifyRestRepositoryIT extends AbstractControllerInte
@Test
public void findAllTestUnAuthorized() throws Exception {
getClient().perform(get("/api/config/submissioncoarnotifyconfigs"))
.andExpect(status().isUnauthorized());
.andExpect(status().isUnauthorized());
}
@Test
@@ -38,18 +38,18 @@ public class SubmissionCOARNotifyRestRepositoryIT extends AbstractControllerInte
String epersonToken = getAuthToken(eperson.getEmail(), password);
getClient(epersonToken).perform(get("/api/config/submissioncoarnotifyconfigs"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.submissioncoarnotifyconfigs", Matchers.containsInAnyOrder(
SubmissionCOARNotifyMatcher.matchCOARNotifyEntry("coarnotify",
List.of("review", "endorsement", "ingest"))
)));
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$._embedded.submissioncoarnotifyconfigs", Matchers.containsInAnyOrder(
SubmissionCOARNotifyMatcher.matchCOARNotifyEntry("coarnotify",
List.of("request-review", "request-endorsement", "request-ingest"))
)));
}
@Test
public void findOneTestUnAuthorized() throws Exception {
getClient().perform(get("/api/config/submissioncoarnotifyconfigs/coarnotify"))
.andExpect(status().isUnauthorized());
.andExpect(status().isUnauthorized());
}
@Test
@@ -57,7 +57,7 @@ public class SubmissionCOARNotifyRestRepositoryIT extends AbstractControllerInte
String epersonToken = getAuthToken(eperson.getEmail(), password);
getClient(epersonToken).perform(get("/api/config/submissioncoarnotifyconfigs/non-existing-coar"))
.andExpect(status().isNotFound());
.andExpect(status().isNotFound());
}
@Test
@@ -65,12 +65,12 @@ public class SubmissionCOARNotifyRestRepositoryIT extends AbstractControllerInte
String epersonToken = getAuthToken(eperson.getEmail(), password);
getClient(epersonToken).perform(get("/api/config/submissioncoarnotifyconfigs/coarnotify"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$", Matchers.is(
SubmissionCOARNotifyMatcher.matchCOARNotifyEntry("coarnotify",
List.of("review", "endorsement", "ingest"))
)));
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$", Matchers.is(
SubmissionCOARNotifyMatcher.matchCOARNotifyEntry("coarnotify",
List.of("request-review", "request-endorsement", "request-ingest"))
)));
}
}

View File

@@ -10,9 +10,9 @@
<map>
<entry key="coarnotify">
<list>
<value>review</value>
<value>endorsement</value>
<value>ingest</value>
<value>request-review</value>
<value>request-endorsement</value>
<value>request-ingest</value>
</list>
</entry>
</map>

View File

@@ -86,6 +86,24 @@
</property>
</bean>
<bean id="org.dspace.qaevent.service.QAEventSecurityService" class="org.dspace.qaevent.service.impl.QAEventSecurityServiceImpl">
<property name="defaultSecurity">
<bean class="org.dspace.qaevent.security.AdministratorsOnlyQASecurity" />
</property>
<property name="qaSecurityConfiguration">
<map>
<entry key="coar-notify" value-ref="submitterQASecurity" />
</map>
</property>
</bean>
<bean id="submitterQASecurity" class="org.dspace.qaevent.security.UserBasedFilterQASecurity">
<property name="filterTemplate">
<!-- we need to escape the { as it as a special meaning for the message format -->
<!-- argument {0} will be replaced with the uuid of the loggedin user -->
<value>'{'!join from=search.resourceid to=resource_uuid fromIndex=${solr.multicorePrefix}search}submitter_authority:{0}</value>
</property>
</bean>
<!--
To configure rules to automatic process specific qaevent you must provide a qaAutomaticProcessingMap
where the keys are the qaevent source provider name and the value is a reference to a