mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 01:54:22 +00:00
Merge branch 'DSpace:main' into w2p-78245_Bugfix-to-UsageReportRestPermissionEvaluatorPlugin
This commit is contained in:
@@ -34,7 +34,7 @@ Past releases are all available via GitHub at https://github.com/DSpace/DSpace/r
|
||||
Documentation for each release may be viewed online or downloaded via our [Documentation Wiki](https://wiki.lyrasis.org/display/DSDOC/).
|
||||
|
||||
The latest DSpace Installation instructions are available at:
|
||||
https://wiki.lyrasis.org/display/DSDOC6x/Installing+DSpace
|
||||
https://wiki.lyrasis.org/display/DSDOC7x/Installing+DSpace
|
||||
|
||||
Please be aware that, as a Java web application, DSpace requires a database (PostgreSQL or Oracle)
|
||||
and a servlet container (usually Tomcat) in order to function.
|
||||
|
@@ -1,6 +1,11 @@
|
||||
version: '3.7'
|
||||
networks:
|
||||
dspacenet:
|
||||
ipam:
|
||||
config:
|
||||
# Define a custom subnet for our DSpace network, so that we can easily trust requests from host to container.
|
||||
# If you customize this value, be sure to customize the 'proxies.trusted.ipranges' in your local.cfg.
|
||||
- subnet: 172.23.0.0/16
|
||||
services:
|
||||
# DSpace (backend) webapp container
|
||||
dspace:
|
||||
|
@@ -12,7 +12,7 @@
|
||||
<parent>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-parent</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -856,6 +856,10 @@
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-jersey-jaxrs</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
|
@@ -8,9 +8,12 @@
|
||||
package org.dspace.app.bulkedit;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.app.util.factory.UtilServiceFactory;
|
||||
import org.dspace.app.util.service.DSpaceObjectUtils;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.MetadataDSpaceCsvExportService;
|
||||
@@ -30,7 +33,7 @@ public class MetadataExport extends DSpaceRunnable<MetadataExportScriptConfigura
|
||||
|
||||
private boolean help = false;
|
||||
private String filename = null;
|
||||
private String handle = null;
|
||||
private String identifier = null;
|
||||
private boolean exportAllMetadata = false;
|
||||
private boolean exportAllItems = false;
|
||||
|
||||
@@ -41,6 +44,8 @@ public class MetadataExport extends DSpaceRunnable<MetadataExportScriptConfigura
|
||||
|
||||
private EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService();
|
||||
|
||||
private DSpaceObjectUtils dSpaceObjectUtils = UtilServiceFactory.getInstance().getDSpaceObjectUtils();
|
||||
|
||||
@Override
|
||||
public void internalRun() throws Exception {
|
||||
|
||||
@@ -57,7 +62,7 @@ public class MetadataExport extends DSpaceRunnable<MetadataExportScriptConfigura
|
||||
handler.handleException(e);
|
||||
}
|
||||
DSpaceCSV dSpaceCSV = metadataDSpaceCsvExportService
|
||||
.handleExport(context, exportAllItems, exportAllMetadata, handle,
|
||||
.handleExport(context, exportAllItems, exportAllMetadata, identifier,
|
||||
handler);
|
||||
handler.writeFilestream(context, filename, dSpaceCSV.getInputStream(), EXPORT_CSV);
|
||||
context.restoreAuthSystemState();
|
||||
@@ -66,7 +71,7 @@ public class MetadataExport extends DSpaceRunnable<MetadataExportScriptConfigura
|
||||
|
||||
protected void logHelpInfo() {
|
||||
handler.logInfo("\nfull export: metadata-export");
|
||||
handler.logInfo("partial export: metadata-export -i handle");
|
||||
handler.logInfo("partial export: metadata-export -i handle/UUID");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -86,7 +91,7 @@ public class MetadataExport extends DSpaceRunnable<MetadataExportScriptConfigura
|
||||
if (!commandLine.hasOption('i')) {
|
||||
exportAllItems = true;
|
||||
}
|
||||
handle = commandLine.getOptionValue('i');
|
||||
identifier = commandLine.getOptionValue('i');
|
||||
filename = getFileNameForExportFile();
|
||||
|
||||
exportAllMetadata = commandLine.hasOption('a');
|
||||
@@ -97,17 +102,20 @@ public class MetadataExport extends DSpaceRunnable<MetadataExportScriptConfigura
|
||||
Context context = new Context();
|
||||
try {
|
||||
DSpaceObject dso = null;
|
||||
if (StringUtils.isNotBlank(handle)) {
|
||||
dso = HandleServiceFactory.getInstance().getHandleService().resolveToObject(context, handle);
|
||||
if (StringUtils.isNotBlank(identifier)) {
|
||||
dso = HandleServiceFactory.getInstance().getHandleService().resolveToObject(context, identifier);
|
||||
if (dso == null) {
|
||||
dso = dSpaceObjectUtils.findDSpaceObject(context, UUID.fromString(identifier));
|
||||
}
|
||||
} else {
|
||||
dso = ContentServiceFactory.getInstance().getSiteService().findSite(context);
|
||||
}
|
||||
if (dso == null) {
|
||||
throw new ParseException("A handle got given that wasn't able to be parsed to a DSpaceObject");
|
||||
throw new ParseException("An identifier was given that wasn't able to be parsed to a DSpaceObject");
|
||||
}
|
||||
return dso.getID().toString() + ".csv";
|
||||
} catch (SQLException e) {
|
||||
handler.handleException("Something went wrong trying to retrieve DSO for handle: " + handle, e);
|
||||
handler.handleException("Something went wrong trying to retrieve DSO for identifier: " + identifier, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@@ -152,8 +152,7 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
|
||||
@Autowired(required = true)
|
||||
protected ConfigurationService configurationService;
|
||||
|
||||
protected final String tempWorkDir
|
||||
= configurationService.getProperty("org.dspace.app.batchitemimport.work.dir");
|
||||
protected String tempWorkDir;
|
||||
|
||||
protected boolean isTest = false;
|
||||
protected boolean isResume = false;
|
||||
@@ -163,6 +162,7 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
tempWorkDir = configurationService.getProperty("org.dspace.app.batchitemimport.work.dir");
|
||||
//Ensure tempWorkDir exists
|
||||
File tempWorkDirFile = new File(tempWorkDir);
|
||||
if (!tempWorkDirFile.exists()) {
|
||||
|
@@ -10,12 +10,16 @@ package org.dspace.app.launcher;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.scripts.DSpaceRunnable;
|
||||
import org.dspace.scripts.configuration.ScriptConfiguration;
|
||||
import org.dspace.scripts.factory.ScriptServiceFactory;
|
||||
@@ -318,11 +322,53 @@ public class ScriptLauncher {
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the commands that the current launcher config file knows about
|
||||
*
|
||||
* Display the commands that are defined in launcher.xml and/or the script service.
|
||||
* @param commandConfigs configs as Document
|
||||
*/
|
||||
private static void display(Document commandConfigs) {
|
||||
// usage
|
||||
System.out.println("Usage: dspace [command-name] {parameters}");
|
||||
|
||||
// commands from launcher.xml
|
||||
Collection<Element> launcherCommands = getLauncherCommands(commandConfigs);
|
||||
if (launcherCommands.size() > 0) {
|
||||
System.out.println("\nCommands from launcher.xml");
|
||||
for (Element command : launcherCommands) {
|
||||
displayCommand(
|
||||
command.getChild("name").getValue(),
|
||||
command.getChild("description").getValue()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// commands from script service
|
||||
Collection<ScriptConfiguration> serviceCommands = getServiceCommands();
|
||||
if (serviceCommands.size() > 0) {
|
||||
System.out.println("\nCommands from script service");
|
||||
for (ScriptConfiguration command : serviceCommands) {
|
||||
displayCommand(
|
||||
command.getName(),
|
||||
command.getDescription()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a single command using a fixed format. Used by {@link #display}.
|
||||
* @param name the name that can be used to invoke the command
|
||||
* @param description the description of the command
|
||||
*/
|
||||
private static void displayCommand(String name, String description) {
|
||||
System.out.format(" - %s: %s\n", name, description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a sorted collection of the commands that are specified in launcher.xml. Used by {@link #display}.
|
||||
* @param commandConfigs the contexts of launcher.xml
|
||||
* @return sorted collection of commands
|
||||
*/
|
||||
private static Collection<Element> getLauncherCommands(Document commandConfigs) {
|
||||
// List all command elements
|
||||
List<Element> commands = commandConfigs.getRootElement().getChildren("command");
|
||||
|
||||
@@ -334,11 +380,32 @@ public class ScriptLauncher {
|
||||
sortedCommands.put(command.getChild("name").getValue(), command);
|
||||
}
|
||||
|
||||
// Display the sorted list
|
||||
System.out.println("Usage: dspace [command-name] {parameters}");
|
||||
for (Element command : sortedCommands.values()) {
|
||||
System.out.println(" - " + command.getChild("name").getValue() +
|
||||
": " + command.getChild("description").getValue());
|
||||
}
|
||||
return sortedCommands.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a sorted collection of the commands that are defined as beans. Used by {@link #display}.
|
||||
* @return sorted collection of commands
|
||||
*/
|
||||
private static Collection<ScriptConfiguration> getServiceCommands() {
|
||||
ScriptService scriptService = ScriptServiceFactory.getInstance().getScriptService();
|
||||
|
||||
Context throwAwayContext = new Context();
|
||||
|
||||
throwAwayContext.turnOffAuthorisationSystem();
|
||||
List<ScriptConfiguration> scriptConfigurations = scriptService.getScriptConfigurations(throwAwayContext);
|
||||
throwAwayContext.restoreAuthSystemState();
|
||||
|
||||
try {
|
||||
throwAwayContext.complete();
|
||||
} catch (SQLException exception) {
|
||||
exception.printStackTrace();
|
||||
throwAwayContext.abort();
|
||||
}
|
||||
|
||||
scriptConfigurations.sort(Comparator.comparing(ScriptConfiguration::getName));
|
||||
|
||||
return scriptConfigurations;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 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.util;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.dspace.app.util.service.DSpaceObjectUtils;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.DSpaceObjectService;
|
||||
import org.dspace.core.Context;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
public class DSpaceObjectUtilsImpl implements DSpaceObjectUtils {
|
||||
|
||||
@Autowired
|
||||
private ContentServiceFactory contentServiceFactory;
|
||||
|
||||
/**
|
||||
* Retrieve a DSpaceObject from its uuid. As this method need to iterate over all the different services that
|
||||
* support concrete class of DSpaceObject it has poor performance. Please consider the use of the direct service
|
||||
* (ItemService, CommunityService, etc.) if you know in advance the type of DSpaceObject that you are looking for
|
||||
*
|
||||
* @param context
|
||||
* DSpace context
|
||||
* @param uuid
|
||||
* the uuid to lookup
|
||||
* @return the DSpaceObject if any with the supplied uuid
|
||||
* @throws SQLException
|
||||
*/
|
||||
public DSpaceObject findDSpaceObject(Context context, UUID uuid) throws SQLException {
|
||||
for (DSpaceObjectService<? extends DSpaceObject> dSpaceObjectService :
|
||||
contentServiceFactory.getDSpaceObjectServices()) {
|
||||
DSpaceObject dso = dSpaceObjectService.find(context, uuid);
|
||||
if (dso != null) {
|
||||
return dso;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
package org.dspace.app.util.factory;
|
||||
|
||||
import org.dspace.app.util.service.DSpaceObjectUtils;
|
||||
import org.dspace.app.util.service.MetadataExposureService;
|
||||
import org.dspace.app.util.service.OpenSearchService;
|
||||
import org.dspace.app.util.service.WebAppService;
|
||||
@@ -25,6 +26,8 @@ public abstract class UtilServiceFactory {
|
||||
|
||||
public abstract MetadataExposureService getMetadataExposureService();
|
||||
|
||||
public abstract DSpaceObjectUtils getDSpaceObjectUtils();
|
||||
|
||||
public static UtilServiceFactory getInstance() {
|
||||
return DSpaceServicesFactory.getInstance().getServiceManager()
|
||||
.getServiceByName("appUtilServiceFactory", UtilServiceFactory.class);
|
||||
|
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
package org.dspace.app.util.factory;
|
||||
|
||||
import org.dspace.app.util.service.DSpaceObjectUtils;
|
||||
import org.dspace.app.util.service.MetadataExposureService;
|
||||
import org.dspace.app.util.service.OpenSearchService;
|
||||
import org.dspace.app.util.service.WebAppService;
|
||||
@@ -26,6 +27,8 @@ public class UtilServiceFactoryImpl extends UtilServiceFactory {
|
||||
private OpenSearchService openSearchService;
|
||||
@Autowired(required = true)
|
||||
private WebAppService webAppService;
|
||||
@Autowired(required = true)
|
||||
private DSpaceObjectUtils dSpaceObjectUtils;
|
||||
|
||||
@Override
|
||||
public WebAppService getWebAppService() {
|
||||
@@ -41,4 +44,9 @@ public class UtilServiceFactoryImpl extends UtilServiceFactory {
|
||||
public MetadataExposureService getMetadataExposureService() {
|
||||
return metadataExposureService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DSpaceObjectUtils getDSpaceObjectUtils() {
|
||||
return dSpaceObjectUtils;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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.util.service;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* Utility class providing methods to deal with generic DSpace Object of unknown type
|
||||
*/
|
||||
public interface DSpaceObjectUtils {
|
||||
/**
|
||||
* Retrieve a DSpaceObject from its uuid. As this method need to iterate over all the different services that
|
||||
* support concrete class of DSpaceObject it has poor performance. Please consider the use of the direct service
|
||||
* (ItemService, CommunityService, etc.) if you know in advance the type of DSpaceObject that you are looking for
|
||||
*
|
||||
* @param context
|
||||
* DSpace context
|
||||
* @param uuid
|
||||
* the uuid to lookup
|
||||
* @return the DSpaceObject if any with the supplied uuid
|
||||
* @throws SQLException
|
||||
*/
|
||||
public DSpaceObject findDSpaceObject(Context context, UUID uuid) throws SQLException;
|
||||
}
|
@@ -16,6 +16,7 @@ import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -480,7 +481,14 @@ public class ShibAuthentication implements AuthenticationMethod {
|
||||
* Get login page to which to redirect. Returns URL (as string) to which to
|
||||
* redirect to obtain credentials (either password prompt or e.g. HTTPS port
|
||||
* for client cert.); null means no redirect.
|
||||
*
|
||||
* <P>
|
||||
* For Shibboleth, this URL looks like (note 'target' param is URL encoded, but shown as unencoded in this example)
|
||||
* [shibURL]?target=[dspace.server.url]/api/authn/shibboleth?redirectUrl=[dspace.ui.url]
|
||||
* <P>
|
||||
* This URL is used by the client to redirect directly to Shibboleth for authentication. The "target" param
|
||||
* is then the location (in REST API) where Shibboleth redirects back to. The "redirectUrl" is the path/URL in the
|
||||
* client (e.g. Angular UI) which the REST API redirects the user to (after capturing/storing any auth info from
|
||||
* Shibboleth).
|
||||
* @param context DSpace context, will be modified (ePerson set) upon success.
|
||||
* @param request The HTTP request that started this operation, or null if not
|
||||
* applicable.
|
||||
@@ -507,8 +515,8 @@ public class ShibAuthentication implements AuthenticationMethod {
|
||||
}
|
||||
|
||||
// Determine the server return URL, where shib will send the user after authenticating.
|
||||
// We need it to go back to DSpace's shibboleth-login url so we will extract the user's information
|
||||
// and locally authenticate them.
|
||||
// We need it to go back to DSpace's ShibbolethRestController so we will extract the user's information,
|
||||
// locally authenticate them & then redirect back to the UI.
|
||||
String returnURL = configurationService.getProperty("dspace.server.url") + "/api/authn/shibboleth"
|
||||
+ ((redirectUrl != null) ? "?redirectUrl=" + redirectUrl : "");
|
||||
|
||||
@@ -533,6 +541,25 @@ public class ShibAuthentication implements AuthenticationMethod {
|
||||
return "shibboleth";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Shibboleth plugin is enabled
|
||||
* @return true if enabled, false otherwise
|
||||
*/
|
||||
public static boolean isEnabled() {
|
||||
final String shibPluginName = new ShibAuthentication().getName();
|
||||
boolean shibEnabled = false;
|
||||
// Loop through all enabled authentication plugins to see if Shibboleth is one of them.
|
||||
Iterator<AuthenticationMethod> authenticationMethodIterator =
|
||||
AuthenticateServiceFactory.getInstance().getAuthenticationService().authenticationMethodIterator();
|
||||
while (authenticationMethodIterator.hasNext()) {
|
||||
if (shibPluginName.equals(authenticationMethodIterator.next().getName())) {
|
||||
shibEnabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return shibEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify an existing EPerson based upon the shibboleth attributes provided on
|
||||
* the request object. There are three cases where this can occurr, each as
|
||||
|
@@ -17,8 +17,10 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang.NotImplementedException;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -238,6 +240,21 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
|
||||
public List<MetadataValue> addMetadata(Context context, T dso, MetadataField metadataField, String lang,
|
||||
List<String> values, List<String> authorities, List<Integer> confidences)
|
||||
throws SQLException {
|
||||
|
||||
//Set place to list length of all metadatavalues for the given schema.element.qualifier combination.
|
||||
// Subtract one to adhere to the 0 as first element rule
|
||||
final Supplier<Integer> placeSupplier = () ->
|
||||
this.getMetadata(dso, metadataField.getMetadataSchema().getName(), metadataField.getElement(),
|
||||
metadataField.getQualifier(), Item.ANY).size() - 1;
|
||||
|
||||
return addMetadata(context, dso, metadataField, lang, values, authorities, confidences, placeSupplier);
|
||||
|
||||
}
|
||||
|
||||
public List<MetadataValue> addMetadata(Context context, T dso, MetadataField metadataField, String lang,
|
||||
List<String> values, List<String> authorities, List<Integer> confidences, Supplier<Integer> placeSupplier)
|
||||
throws SQLException {
|
||||
|
||||
boolean authorityControlled = metadataAuthorityService.isAuthorityControlled(metadataField);
|
||||
boolean authorityRequired = metadataAuthorityService.isAuthorityRequired(metadataField);
|
||||
List<MetadataValue> newMetadata = new ArrayList<>(values.size());
|
||||
@@ -252,11 +269,8 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
|
||||
}
|
||||
MetadataValue metadataValue = metadataValueService.create(context, dso, metadataField);
|
||||
newMetadata.add(metadataValue);
|
||||
//Set place to list length of all metadatavalues for the given schema.element.qualifier combination.
|
||||
// Subtract one to adhere to the 0 as first element rule
|
||||
metadataValue.setPlace(
|
||||
this.getMetadata(dso, metadataField.getMetadataSchema().getName(), metadataField.getElement(),
|
||||
metadataField.getQualifier(), Item.ANY).size() - 1);
|
||||
|
||||
metadataValue.setPlace(placeSupplier.get());
|
||||
|
||||
metadataValue.setLanguage(lang == null ? null : lang.trim());
|
||||
|
||||
@@ -359,7 +373,7 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
|
||||
public MetadataValue addMetadata(Context context, T dso, String schema, String element, String qualifier,
|
||||
String lang, String value, String authority, int confidence) throws SQLException {
|
||||
return addMetadata(context, dso, schema, element, qualifier, lang, Arrays.asList(value),
|
||||
Arrays.asList(authority), Arrays.asList(confidence)).get(0);
|
||||
Arrays.asList(authority), Arrays.asList(confidence)).stream().findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -805,4 +819,12 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
|
||||
dso.setMetadataModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetadataValue addMetadata(Context context, T dso, String schema, String element, String qualifier,
|
||||
String lang, String value, String authority, int confidence, int place) throws SQLException {
|
||||
|
||||
throw new NotImplementedException();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -11,12 +11,14 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -1405,5 +1407,25 @@ prevent the generation of resource policy entry values with null dspace_object a
|
||||
return listToReturn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetadataValue addMetadata(Context context, Item dso, String schema, String element, String qualifier,
|
||||
String lang, String value, String authority, int confidence, int place) throws SQLException {
|
||||
|
||||
// We will not verify that they are valid entries in the registry
|
||||
// until update() is called.
|
||||
MetadataField metadataField = metadataFieldService.findByElement(context, schema, element, qualifier);
|
||||
if (metadataField == null) {
|
||||
throw new SQLException(
|
||||
"bad_dublin_core schema=" + schema + "." + element + "." + qualifier + ". Metadata field does not " +
|
||||
"exist!");
|
||||
}
|
||||
|
||||
final Supplier<Integer> placeSupplier = () -> place;
|
||||
|
||||
return addMetadata(context, dso, metadataField, lang, Arrays.asList(value),
|
||||
Arrays.asList(authority), Arrays.asList(confidence), placeSupplier)
|
||||
.stream().findFirst().orElse(null);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -12,9 +12,11 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.common.collect.Iterators;
|
||||
import org.dspace.app.bulkedit.DSpaceCSV;
|
||||
import org.dspace.app.util.service.DSpaceObjectUtils;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.content.service.MetadataDSpaceCsvExportService;
|
||||
import org.dspace.core.Constants;
|
||||
@@ -31,8 +33,11 @@ public class MetadataDSpaceCsvExportServiceImpl implements MetadataDSpaceCsvExpo
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
|
||||
@Autowired
|
||||
private DSpaceObjectUtils dSpaceObjectUtils;
|
||||
|
||||
@Override
|
||||
public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean exportAllMetadata, String handle,
|
||||
public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean exportAllMetadata, String identifier,
|
||||
DSpaceRunnableHandler handler) throws Exception {
|
||||
Iterator<Item> toExport = null;
|
||||
|
||||
@@ -40,26 +45,32 @@ public class MetadataDSpaceCsvExportServiceImpl implements MetadataDSpaceCsvExpo
|
||||
handler.logInfo("Exporting whole repository WARNING: May take some time!");
|
||||
toExport = itemService.findAll(context);
|
||||
} else {
|
||||
DSpaceObject dso = HandleServiceFactory.getInstance().getHandleService().resolveToObject(context, handle);
|
||||
DSpaceObject dso = HandleServiceFactory.getInstance().getHandleService()
|
||||
.resolveToObject(context, identifier);
|
||||
if (dso == null) {
|
||||
dso = dSpaceObjectUtils.findDSpaceObject(context, UUID.fromString(identifier));
|
||||
}
|
||||
if (dso == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Item '" + handle + "' does not resolve to an item in your repository!");
|
||||
"DSO '" + identifier + "' does not resolve to a DSpace Object in your repository!");
|
||||
}
|
||||
|
||||
if (dso.getType() == Constants.ITEM) {
|
||||
handler.logInfo("Exporting item '" + dso.getName() + "' (" + handle + ")");
|
||||
handler.logInfo("Exporting item '" + dso.getName() + "' (" + identifier + ")");
|
||||
List<Item> item = new ArrayList<>();
|
||||
item.add((Item) dso);
|
||||
toExport = item.iterator();
|
||||
} else if (dso.getType() == Constants.COLLECTION) {
|
||||
handler.logInfo("Exporting collection '" + dso.getName() + "' (" + handle + ")");
|
||||
handler.logInfo("Exporting collection '" + dso.getName() + "' (" + identifier + ")");
|
||||
Collection collection = (Collection) dso;
|
||||
toExport = itemService.findByCollection(context, collection);
|
||||
} else if (dso.getType() == Constants.COMMUNITY) {
|
||||
handler.logInfo("Exporting community '" + dso.getName() + "' (" + handle + ")");
|
||||
handler.logInfo("Exporting community '" + dso.getName() + "' (" + identifier + ")");
|
||||
toExport = buildFromCommunity(context, (Community) dso);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Error identifying '" + handle + "'");
|
||||
throw new IllegalArgumentException(
|
||||
String.format("DSO with id '%s' (type: %s) can't be exported. Supported types: %s", identifier,
|
||||
Constants.typeText[dso.getType()], "Item | Collection | Community"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -367,6 +367,30 @@ public interface DSpaceObjectService<T extends DSpaceObject> {
|
||||
public MetadataValue addMetadata(Context context, T dso, String schema, String element, String qualifier,
|
||||
String lang, String value) throws SQLException;
|
||||
|
||||
/**
|
||||
* Add a single metadata value at the given place position.
|
||||
*
|
||||
* @param context DSpace context
|
||||
* @param dso DSpaceObject
|
||||
* @param schema the schema for the metadata field. <em>Must</em> match
|
||||
* the <code>name</code> of an existing metadata schema.
|
||||
* @param element the metadata element name
|
||||
* @param qualifier the metadata qualifier, or <code>null</code> for
|
||||
* unqualified
|
||||
* @param lang the ISO639 language code, optionally followed by an underscore
|
||||
* and the ISO3166 country code. <code>null</code> means the
|
||||
* value has no language (for example, a date).
|
||||
* @param value the value to add.
|
||||
* @param authority the external authority key for this value (or null)
|
||||
* @param confidence the authority confidence (default 0)
|
||||
* @param place the metadata position
|
||||
* @return the MetadataValue added ot the object
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public MetadataValue addMetadata(Context context, T dso, String schema, String element, String qualifier,
|
||||
String lang, String value, String authority, int confidence, int place) throws SQLException;
|
||||
|
||||
|
||||
/**
|
||||
* Add a single metadata field. This is appended to existing
|
||||
* values. Use <code>clearMetadata</code> to remove values.
|
||||
|
@@ -741,5 +741,4 @@ public interface ItemService
|
||||
public List<MetadataValue> getMetadata(Item item, String schema, String element, String qualifier,
|
||||
String lang, boolean enableVirtualMetadata);
|
||||
|
||||
|
||||
}
|
||||
|
@@ -28,12 +28,13 @@ public interface MetadataDSpaceCsvExportService {
|
||||
* @param context The relevant DSpace context
|
||||
* @param exportAllItems A boolean indicating whether or not the entire repository should be exported
|
||||
* @param exportAllMetadata Defines if all metadata should be exported or only the allowed ones
|
||||
* @param handle The handle for the DSpaceObject to be exported, can be a Community, Collection or Item
|
||||
* @param identifier The handle or UUID for the DSpaceObject to be exported, can be a Community,
|
||||
* Collection or Item
|
||||
* @return A DSpaceCSV object containing the exported information
|
||||
* @throws Exception If something goes wrong
|
||||
*/
|
||||
public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean exportAllMetadata,
|
||||
String handle, DSpaceRunnableHandler dSpaceRunnableHandler) throws Exception;
|
||||
String identifier, DSpaceRunnableHandler dSpaceRunnableHandler) throws Exception;
|
||||
|
||||
/**
|
||||
* This method will export all the Items in the given toExport iterator to a DSpaceCSV
|
||||
|
@@ -12,6 +12,7 @@ import java.util.List;
|
||||
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.discovery.configuration.DiscoveryConfiguration;
|
||||
import org.dspace.discovery.configuration.DiscoveryMoreLikeThisConfiguration;
|
||||
import org.dspace.discovery.configuration.DiscoverySearchFilterFacet;
|
||||
|
||||
@@ -62,11 +63,14 @@ public interface SearchService {
|
||||
* @param field the field of the filter query
|
||||
* @param operator equals/notequals/notcontains/authority/notauthority
|
||||
* @param value the filter query value
|
||||
* @param config (nullable) the discovery configuration (if not null, field's corresponding facet.type checked to
|
||||
* be standard so suffix is not added for equals operator)
|
||||
* @return a filter query
|
||||
* @throws SQLException if database error
|
||||
* An exception that provides information on a database access error or other errors.
|
||||
*/
|
||||
DiscoverFilterQuery toFilterQuery(Context context, String field, String operator, String value) throws SQLException;
|
||||
DiscoverFilterQuery toFilterQuery(Context context, String field, String operator, String value,
|
||||
DiscoveryConfiguration config) throws SQLException;
|
||||
|
||||
List<Item> getRelatedItems(Context context, Item item,
|
||||
DiscoveryMoreLikeThisConfiguration moreLikeThisConfiguration);
|
||||
|
@@ -60,6 +60,7 @@ import org.dspace.core.Context;
|
||||
import org.dspace.core.Email;
|
||||
import org.dspace.core.I18nUtil;
|
||||
import org.dspace.core.LogManager;
|
||||
import org.dspace.discovery.configuration.DiscoveryConfiguration;
|
||||
import org.dspace.discovery.configuration.DiscoveryConfigurationParameters;
|
||||
import org.dspace.discovery.configuration.DiscoveryMoreLikeThisConfiguration;
|
||||
import org.dspace.discovery.configuration.DiscoverySearchFilterFacet;
|
||||
@@ -1069,9 +1070,9 @@ public class SolrServiceImpl implements SearchService, IndexingService {
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiscoverFilterQuery toFilterQuery(Context context, String field, String operator, String value)
|
||||
public DiscoverFilterQuery toFilterQuery(Context context, String field, String operator, String value,
|
||||
DiscoveryConfiguration config)
|
||||
throws SQLException {
|
||||
DiscoverFilterQuery result = new DiscoverFilterQuery();
|
||||
|
||||
@@ -1081,7 +1082,14 @@ public class SolrServiceImpl implements SearchService, IndexingService {
|
||||
|
||||
|
||||
if (operator.endsWith("equals")) {
|
||||
filterQuery.append("_keyword");
|
||||
final boolean isStandardField
|
||||
= Optional.ofNullable(config)
|
||||
.flatMap(c -> Optional.ofNullable(c.getSidebarFacet(field)))
|
||||
.map(facet -> facet.getType().equals(DiscoveryConfigurationParameters.TYPE_STANDARD))
|
||||
.orElse(false);
|
||||
if (!isStandardField) {
|
||||
filterQuery.append("_keyword");
|
||||
}
|
||||
} else if (operator.endsWith("authority")) {
|
||||
filterQuery.append("_authority");
|
||||
}
|
||||
|
@@ -20,26 +20,8 @@ public class DiscoverySortConfiguration {
|
||||
|
||||
public static final String SCORE = "score";
|
||||
|
||||
/** Attributes used for sorting of results **/
|
||||
public enum SORT_ORDER {
|
||||
desc,
|
||||
asc
|
||||
}
|
||||
|
||||
private DiscoverySortFieldConfiguration defaultSort = null;
|
||||
|
||||
private List<DiscoverySortFieldConfiguration> sortFields = new ArrayList<DiscoverySortFieldConfiguration>();
|
||||
|
||||
private SORT_ORDER defaultSortOrder = SORT_ORDER.desc;
|
||||
|
||||
public DiscoverySortFieldConfiguration getDefaultSort() {
|
||||
return defaultSort;
|
||||
}
|
||||
|
||||
public void setDefaultSort(DiscoverySortFieldConfiguration defaultSort) {
|
||||
this.defaultSort = defaultSort;
|
||||
}
|
||||
|
||||
public List<DiscoverySortFieldConfiguration> getSortFields() {
|
||||
return sortFields;
|
||||
}
|
||||
@@ -48,14 +30,6 @@ public class DiscoverySortConfiguration {
|
||||
this.sortFields = sortFields;
|
||||
}
|
||||
|
||||
public SORT_ORDER getDefaultSortOrder() {
|
||||
return defaultSortOrder;
|
||||
}
|
||||
|
||||
public void setDefaultSortOrder(SORT_ORDER defaultSortOrder) {
|
||||
this.defaultSortOrder = defaultSortOrder;
|
||||
}
|
||||
|
||||
public DiscoverySortFieldConfiguration getSortFieldConfiguration(String sortField) {
|
||||
if (StringUtils.isBlank(sortField)) {
|
||||
return null;
|
||||
@@ -67,10 +41,6 @@ public class DiscoverySortConfiguration {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
if (defaultSort != null && StringUtils.equals(defaultSort.getMetadataField(), sortField)) {
|
||||
return defaultSort;
|
||||
}
|
||||
|
||||
for (DiscoverySortFieldConfiguration sortFieldConfiguration : CollectionUtils.emptyIfNull(sortFields)) {
|
||||
if (StringUtils.equals(sortFieldConfiguration.getMetadataField(), sortField)) {
|
||||
return sortFieldConfiguration;
|
||||
|
@@ -18,11 +18,18 @@ public class DiscoverySortFieldConfiguration {
|
||||
private String metadataField;
|
||||
private String type = DiscoveryConfigurationParameters.TYPE_TEXT;
|
||||
|
||||
/** Attributes used for sorting of results **/
|
||||
public enum SORT_ORDER {
|
||||
desc,
|
||||
asc
|
||||
}
|
||||
|
||||
private SORT_ORDER defaultSortOrder;
|
||||
|
||||
public String getMetadataField() {
|
||||
return metadataField;
|
||||
}
|
||||
|
||||
@Autowired(required = true)
|
||||
public void setMetadataField(String metadataField) {
|
||||
this.metadataField = metadataField;
|
||||
}
|
||||
@@ -35,6 +42,15 @@ public class DiscoverySortFieldConfiguration {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public SORT_ORDER getDefaultSortOrder() {
|
||||
return defaultSortOrder;
|
||||
}
|
||||
|
||||
@Autowired(required = true)
|
||||
public void setDefaultSortOrder(SORT_ORDER defaultSortOrder) {
|
||||
this.defaultSortOrder = defaultSortOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj != null && obj instanceof DiscoverySortFieldConfiguration) {
|
||||
|
@@ -47,8 +47,6 @@ BEGIN
|
||||
updateseq('fileextension_seq', 'fileextension', 'file_extension_id');
|
||||
updateseq('resourcepolicy_seq', 'resourcepolicy', 'policy_id');
|
||||
updateseq('workspaceitem_seq', 'workspaceitem', 'workspace_item_id');
|
||||
updateseq('workflowitem_seq', 'workflowitem', 'workflow_id');
|
||||
updateseq('tasklistitem_seq', 'tasklistitem', 'tasklist_id');
|
||||
updateseq('registrationdata_seq', 'registrationdata',
|
||||
'registrationdata_id');
|
||||
updateseq('subscription_seq', 'subscription', 'subscription_id');
|
||||
|
@@ -24,8 +24,6 @@ SELECT setval('bitstreamformatregistry_seq', max(bitstream_format_id)) FROM bits
|
||||
SELECT setval('fileextension_seq', max(file_extension_id)) FROM fileextension;
|
||||
SELECT setval('resourcepolicy_seq', max(policy_id)) FROM resourcepolicy;
|
||||
SELECT setval('workspaceitem_seq', max(workspace_item_id)) FROM workspaceitem;
|
||||
SELECT setval('workflowitem_seq', max(workflow_id)) FROM workflowitem;
|
||||
SELECT setval('tasklistitem_seq', max(tasklist_id)) FROM tasklistitem;
|
||||
SELECT setval('registrationdata_seq', max(registrationdata_id)) FROM registrationdata;
|
||||
SELECT setval('subscription_seq', max(subscription_id)) FROM subscription;
|
||||
SELECT setval('metadatafieldregistry_seq', max(metadata_field_id)) FROM metadatafieldregistry;
|
||||
|
@@ -46,9 +46,11 @@
|
||||
<required></required>
|
||||
</field>
|
||||
</row>
|
||||
</form>
|
||||
|
||||
</form>
|
||||
|
||||
<form name="traditionalpageone">
|
||||
<!-- NOTE: this Author <row> is customized from the default submission-forms.xml in order to make it
|
||||
easier to test Entity functionality -->
|
||||
<row>
|
||||
<relation-field>
|
||||
<relationship-type>isAuthorOfPublication</relationship-type>
|
||||
@@ -182,7 +184,7 @@ it, please enter the types and the actual numbers or codes.</hint>
|
||||
</field>
|
||||
</row>
|
||||
</form>
|
||||
|
||||
|
||||
<form name="traditionalpagetwo">
|
||||
<row>
|
||||
<field>
|
||||
@@ -310,7 +312,7 @@ it, please enter the types and the actual numbers or codes.</hint>
|
||||
</field>
|
||||
</row>
|
||||
</form>
|
||||
|
||||
|
||||
<form name="sampleauthority">
|
||||
<row>
|
||||
<field>
|
||||
@@ -347,7 +349,7 @@ it, please enter the types and the actual numbers or codes.</hint>
|
||||
<required></required>
|
||||
</field>
|
||||
</row>
|
||||
</form>
|
||||
</form>
|
||||
</form-definitions>
|
||||
|
||||
|
||||
|
@@ -12,9 +12,11 @@ import static junit.framework.TestCase.assertTrue;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.AbstractIntegrationTestWithDatabase;
|
||||
import org.dspace.app.launcher.ScriptLauncher;
|
||||
import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler;
|
||||
@@ -24,6 +26,7 @@ import org.dspace.builder.ItemBuilder;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.scripts.DSpaceRunnable;
|
||||
import org.dspace.scripts.configuration.ScriptConfiguration;
|
||||
import org.dspace.scripts.factory.ScriptServiceFactory;
|
||||
@@ -100,4 +103,148 @@ public class MetadataExportIT
|
||||
script.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metadataExportToCsvTestUUID() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
Community community = CommunityBuilder.createCommunity(context)
|
||||
.build();
|
||||
Collection collection = CollectionBuilder.createCollection(context, community)
|
||||
.build();
|
||||
Item item = ItemBuilder.createItem(context, collection)
|
||||
.withAuthor("Donald, Smith")
|
||||
.build();
|
||||
context.restoreAuthSystemState();
|
||||
String fileLocation = configurationService.getProperty("dspace.dir")
|
||||
+ testProps.get("test.exportcsv").toString();
|
||||
|
||||
String[] args = new String[] {"metadata-export",
|
||||
"-i", String.valueOf(item.getID()),
|
||||
"-f", fileLocation};
|
||||
TestDSpaceRunnableHandler testDSpaceRunnableHandler
|
||||
= new TestDSpaceRunnableHandler();
|
||||
|
||||
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl),
|
||||
testDSpaceRunnableHandler, kernelImpl);
|
||||
File file = new File(fileLocation);
|
||||
String fileContent = IOUtils.toString(new FileInputStream(file), StandardCharsets.UTF_8);
|
||||
assertTrue(fileContent.contains("Donald, Smith"));
|
||||
assertTrue(fileContent.contains(String.valueOf(item.getID())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metadataExportToCsvTestUUIDParent() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
Community community = CommunityBuilder.createCommunity(context)
|
||||
.build();
|
||||
Collection collection = CollectionBuilder.createCollection(context, community)
|
||||
.build();
|
||||
Item item = ItemBuilder.createItem(context, collection)
|
||||
.withAuthor("Donald, Smith")
|
||||
.build();
|
||||
context.restoreAuthSystemState();
|
||||
String fileLocation = configurationService.getProperty("dspace.dir")
|
||||
+ testProps.get("test.exportcsv").toString();
|
||||
|
||||
String[] args = new String[] {"metadata-export",
|
||||
"-i", String.valueOf(collection.getID()),
|
||||
"-f", fileLocation};
|
||||
TestDSpaceRunnableHandler testDSpaceRunnableHandler
|
||||
= new TestDSpaceRunnableHandler();
|
||||
|
||||
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl),
|
||||
testDSpaceRunnableHandler, kernelImpl);
|
||||
File file = new File(fileLocation);
|
||||
String fileContent = IOUtils.toString(new FileInputStream(file), StandardCharsets.UTF_8);
|
||||
assertTrue(fileContent.contains("Donald, Smith"));
|
||||
assertTrue(fileContent.contains(String.valueOf(item.getID())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metadataExportToCsvTestUUIDGrandParent() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
Community community = CommunityBuilder.createCommunity(context)
|
||||
.build();
|
||||
Collection collection = CollectionBuilder.createCollection(context, community)
|
||||
.build();
|
||||
Item item = ItemBuilder.createItem(context, collection)
|
||||
.withAuthor("Donald, Smith")
|
||||
.build();
|
||||
context.restoreAuthSystemState();
|
||||
String fileLocation = configurationService.getProperty("dspace.dir")
|
||||
+ testProps.get("test.exportcsv").toString();
|
||||
|
||||
String[] args = new String[] {"metadata-export",
|
||||
"-i", String.valueOf(community.getID()),
|
||||
"-f", fileLocation};
|
||||
TestDSpaceRunnableHandler testDSpaceRunnableHandler
|
||||
= new TestDSpaceRunnableHandler();
|
||||
|
||||
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl),
|
||||
testDSpaceRunnableHandler, kernelImpl);
|
||||
File file = new File(fileLocation);
|
||||
String fileContent = IOUtils.toString(new FileInputStream(file), StandardCharsets.UTF_8);
|
||||
assertTrue(fileContent.contains("Donald, Smith"));
|
||||
assertTrue(fileContent.contains(String.valueOf(item.getID())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metadataExportToCsvTest_NonValidIdentifier() throws Exception {
|
||||
String fileLocation = configurationService.getProperty("dspace.dir")
|
||||
+ testProps.get("test.exportcsv").toString();
|
||||
|
||||
String nonValidUUID = String.valueOf(UUID.randomUUID());
|
||||
String[] args = new String[] {"metadata-export", "-i", nonValidUUID, "-f", fileLocation};
|
||||
TestDSpaceRunnableHandler testDSpaceRunnableHandler
|
||||
= new TestDSpaceRunnableHandler();
|
||||
|
||||
ScriptService scriptService = ScriptServiceFactory.getInstance().getScriptService();
|
||||
ScriptConfiguration scriptConfiguration = scriptService.getScriptConfiguration(args[0]);
|
||||
|
||||
DSpaceRunnable script = null;
|
||||
if (scriptConfiguration != null) {
|
||||
script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration);
|
||||
}
|
||||
if (script != null) {
|
||||
script.initialize(args, testDSpaceRunnableHandler, null);
|
||||
script.run();
|
||||
}
|
||||
|
||||
Exception exceptionDuringTestRun = testDSpaceRunnableHandler.getException();
|
||||
assertTrue("Random UUID caused IllegalArgumentException",
|
||||
exceptionDuringTestRun instanceof IllegalArgumentException);
|
||||
assertTrue("IllegalArgumentException contains mention of the non-valid UUID",
|
||||
StringUtils.contains(exceptionDuringTestRun.getMessage(), nonValidUUID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metadataExportToCsvTest_NonValidDSOType() throws Exception {
|
||||
String fileLocation = configurationService.getProperty("dspace.dir")
|
||||
+ testProps.get("test.exportcsv").toString();
|
||||
|
||||
String uuidNonValidDSOType = String.valueOf(eperson.getID());
|
||||
String[] args = new String[] {"metadata-export", "-i", uuidNonValidDSOType, "-f", fileLocation};
|
||||
TestDSpaceRunnableHandler testDSpaceRunnableHandler
|
||||
= new TestDSpaceRunnableHandler();
|
||||
|
||||
ScriptService scriptService = ScriptServiceFactory.getInstance().getScriptService();
|
||||
ScriptConfiguration scriptConfiguration = scriptService.getScriptConfiguration(args[0]);
|
||||
|
||||
DSpaceRunnable script = null;
|
||||
if (scriptConfiguration != null) {
|
||||
script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration);
|
||||
}
|
||||
if (script != null) {
|
||||
script.initialize(args, testDSpaceRunnableHandler, null);
|
||||
script.run();
|
||||
}
|
||||
|
||||
Exception exceptionDuringTestRun = testDSpaceRunnableHandler.getException();
|
||||
assertTrue("UUID of non-supported dsoType IllegalArgumentException",
|
||||
exceptionDuringTestRun instanceof IllegalArgumentException);
|
||||
assertTrue("IllegalArgumentException contains mention of the UUID of non-supported dsoType",
|
||||
StringUtils.contains(exceptionDuringTestRun.getMessage(), uuidNonValidDSOType));
|
||||
assertTrue("IllegalArgumentException contains mention of the non-supported dsoType",
|
||||
StringUtils.contains(exceptionDuringTestRun.getMessage(), Constants.typeText[eperson.getType()]));
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<artifactId>dspace-parent</artifactId>
|
||||
<groupId>org.dspace</groupId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-parent</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-rest</artifactId>
|
||||
<packaging>war</packaging>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<name>DSpace (Deprecated) REST Webapp</name>
|
||||
<description>DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED.
|
||||
Please consider using the REST API in the dspace-server-webapp instead!</description>
|
||||
@@ -12,7 +12,7 @@
|
||||
<parent>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-parent</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@@ -15,7 +15,7 @@
|
||||
<parent>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-parent</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@@ -14,6 +14,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.app.rest.model.AuthnRest;
|
||||
import org.dspace.authenticate.ShibAuthentication;
|
||||
import org.dspace.core.Utils;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.slf4j.Logger;
|
||||
@@ -27,10 +28,27 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Rest controller that handles redirect after shibboleth authentication succeded
|
||||
* Rest controller that handles redirect *after* shibboleth authentication succeeded.
|
||||
* <P>
|
||||
* Shibboleth authentication does NOT occur in this Controller, but occurs before this class is called.
|
||||
* The general Shibboleth login process is as follows:
|
||||
* 1. When Shibboleth plugin is enabled, client/UI receives Shibboleth's absolute URL in WWW-Authenticate header.
|
||||
* See {@link org.dspace.authenticate.ShibAuthentication} loginPageURL() method.
|
||||
* 2. Client sends the user to that URL when they select Shibboleth authentication.
|
||||
* 3. User logs in using Shibboleth
|
||||
* 4. If successful, they are redirected by Shibboleth to this Controller (the path of this controller is passed
|
||||
* to Shibboleth as a URL param in step 1)
|
||||
* 5. NOTE: Prior to hitting this Controller, {@link org.dspace.app.rest.security.ShibbolethAuthenticationFilter}
|
||||
* briefly intercepts the request in order to check for a valid Shibboleth login (see
|
||||
* ShibAuthentication.authenticate()) and store that user info in a JWT.
|
||||
* 6. This Controller then gets the request & looks for a "redirectUrl" param (also a part of the original URL from
|
||||
* step 1), and redirects the user to that location (after verifying it's a trusted URL). Usually this is a
|
||||
* redirect back to the Client/UI page where the User started.
|
||||
*
|
||||
* @author Andrea Bollini (andrea dot bollini at 4science dot it)
|
||||
* @author Giuseppe Digilio (giuseppe dot digilio at 4science dot it)
|
||||
* @see ShibAuthentication
|
||||
* @see org.dspace.app.rest.security.ShibbolethAuthenticationFilter
|
||||
*/
|
||||
@RequestMapping(value = "/api/" + AuthnRest.CATEGORY + "/shibboleth")
|
||||
@RestController
|
||||
@@ -56,7 +74,11 @@ public class ShibbolethRestController implements InitializingBean {
|
||||
@RequestMapping(method = RequestMethod.GET)
|
||||
public void shibboleth(HttpServletResponse response,
|
||||
@RequestParam(name = "redirectUrl", required = false) String redirectUrl) throws IOException {
|
||||
if (redirectUrl == null) {
|
||||
// NOTE: By the time we get here, we already know that Shibboleth is enabled & authentication succeeded,
|
||||
// as both of those are verified by ShibbolethAuthenticationFilter which runs before this controller
|
||||
|
||||
// If redirectUrl unspecified, default to the configured UI
|
||||
if (StringUtils.isEmpty(redirectUrl)) {
|
||||
redirectUrl = configurationService.getProperty("dspace.ui.url");
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.rest.authorization.impl;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.dspace.app.rest.authorization.AuthorizationFeature;
|
||||
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
|
||||
import org.dspace.app.rest.model.BaseObjectRest;
|
||||
import org.dspace.app.rest.model.ItemRest;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* The create version feature. It can be used to verify if the user can create the version of an Item.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
|
||||
*/
|
||||
@Component
|
||||
@AuthorizationFeatureDocumentation(name = CanCreateVersionFeature.NAME,
|
||||
description = "It can be used to verify if the user can create a new version of an Item")
|
||||
public class CanCreateVersionFeature implements AuthorizationFeature {
|
||||
|
||||
public static final String NAME = "canCreateVersion";
|
||||
|
||||
@Autowired
|
||||
private ConfigurationService configurationService;
|
||||
|
||||
@Autowired
|
||||
private AuthorizeService authorizeService;
|
||||
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
|
||||
@Override
|
||||
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
|
||||
if (object instanceof ItemRest) {
|
||||
EPerson currentUser = context.getCurrentUser();
|
||||
if (Objects.isNull(currentUser)) {
|
||||
return false;
|
||||
}
|
||||
if (authorizeService.isAdmin(context)) {
|
||||
return true;
|
||||
}
|
||||
if (configurationService.getBooleanProperty("versioning.submitterCanCreateNewVersion")) {
|
||||
Item item = itemService.find(context, UUID.fromString(((ItemRest) object).getUuid()));
|
||||
EPerson submitter = item.getSubmitter();
|
||||
return Objects.nonNull(submitter) && currentUser.getID().equals(submitter.getID());
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedTypes() {
|
||||
return new String[]{
|
||||
ItemRest.CATEGORY + "." + ItemRest.NAME
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.rest.authorization.impl;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.dspace.app.rest.authorization.AuthorizationFeature;
|
||||
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
|
||||
import org.dspace.app.rest.model.BaseObjectRest;
|
||||
import org.dspace.app.rest.model.ItemRest;
|
||||
import org.dspace.app.rest.utils.Utils;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* The manageBitstreamBundles feature. It can be used to verify
|
||||
* if the user can manage (ADD | REMOVE) the bundles of bitstreams of an Item.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
|
||||
*/
|
||||
@Component
|
||||
@AuthorizationFeatureDocumentation(name = CanManageBitstreamBundlesFeature.NAME,
|
||||
description = "It can be used to verify if the user can manage (ADD | REMOVE) the bundles of bitstreams of an Item")
|
||||
public class CanManageBitstreamBundlesFeature implements AuthorizationFeature {
|
||||
|
||||
public static final String NAME = "canManageBitstreamBundles";
|
||||
|
||||
@Autowired
|
||||
private AuthorizeService authorizeService;
|
||||
|
||||
@Autowired
|
||||
private Utils utils;
|
||||
|
||||
@Override
|
||||
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
|
||||
if (object instanceof ItemRest) {
|
||||
DSpaceObject dSpaceObject = (DSpaceObject) utils.getDSpaceAPIObjectFromRest(context, object);
|
||||
boolean hasRemovePermission = authorizeService.authorizeActionBoolean(context, context.getCurrentUser(),
|
||||
dSpaceObject, Constants.REMOVE, true);
|
||||
boolean hasAddPermission = authorizeService.authorizeActionBoolean(context, context.getCurrentUser(),
|
||||
dSpaceObject, Constants.ADD, true);
|
||||
return (hasRemovePermission && hasAddPermission);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedTypes() {
|
||||
return new String[]{
|
||||
ItemRest.CATEGORY + "." + ItemRest.NAME
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@@ -8,16 +8,25 @@
|
||||
package org.dspace.app.rest.authorization.impl;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.dspace.app.rest.authorization.AuthorizationFeature;
|
||||
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
|
||||
import org.dspace.app.rest.model.BaseObjectRest;
|
||||
import org.dspace.app.rest.model.CollectionRest;
|
||||
import org.dspace.app.rest.model.ItemRest;
|
||||
import org.dspace.app.rest.utils.Utils;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.service.CollectionService;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.discovery.SearchServiceException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -27,11 +36,11 @@ import org.springframework.stereotype.Component;
|
||||
* Authorization is granted if the current user has ADD and WRITE permissions on the given Collection.
|
||||
*/
|
||||
@Component
|
||||
@AuthorizationFeatureDocumentation(name = ManageMappedItemsFeature.NAME,
|
||||
@AuthorizationFeatureDocumentation(name = CanManageMappingsFeature.NAME,
|
||||
description = "It can be used to verify if mapped items can be listed, searched, added and removed")
|
||||
public class ManageMappedItemsFeature implements AuthorizationFeature {
|
||||
public class CanManageMappingsFeature implements AuthorizationFeature {
|
||||
|
||||
public final static String NAME = "canManageMappedItems";
|
||||
public final static String NAME = "canManageMappings";
|
||||
|
||||
@Autowired
|
||||
private AuthorizeService authorizeService;
|
||||
@@ -39,6 +48,12 @@ public class ManageMappedItemsFeature implements AuthorizationFeature {
|
||||
@Autowired
|
||||
private Utils utils;
|
||||
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
|
||||
@Autowired
|
||||
private CollectionService collectionService;
|
||||
|
||||
@Override
|
||||
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
|
||||
if (object instanceof CollectionRest) {
|
||||
@@ -49,13 +64,26 @@ public class ManageMappedItemsFeature implements AuthorizationFeature {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (object instanceof ItemRest) {
|
||||
Item item = itemService.find(context, UUID.fromString(((ItemRest) object).getUuid()));
|
||||
try {
|
||||
List<Collection> collections = collectionService.findCollectionsWithSubmit("", context, null, 0, 2)
|
||||
.stream()
|
||||
.filter(c -> !c.getID().equals(item.getOwningCollection().getID()))
|
||||
.collect(Collectors.toList());
|
||||
return CollectionUtils.isNotEmpty(collections);
|
||||
} catch (SearchServiceException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedTypes() {
|
||||
return new String[]{
|
||||
CollectionRest.CATEGORY + "." + CollectionRest.NAME
|
||||
CollectionRest.CATEGORY + "." + CollectionRest.NAME,
|
||||
ItemRest.CATEGORY + "." + ItemRest.NAME
|
||||
};
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.rest.authorization.impl;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.dspace.app.rest.authorization.AuthorizationFeature;
|
||||
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
|
||||
import org.dspace.app.rest.model.BaseObjectRest;
|
||||
import org.dspace.app.rest.model.ItemRest;
|
||||
import org.dspace.app.rest.utils.Utils;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* The CanManageRelationshipsFeature feature. It can be used to verify
|
||||
* if the user has WRITE permission on the Item.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
|
||||
*/
|
||||
@Component
|
||||
@AuthorizationFeatureDocumentation(name = CanManageRelationshipsFeature.NAME,
|
||||
description = "It can be used to verify if the user has permissions to manage relationships of the Item")
|
||||
public class CanManageRelationshipsFeature implements AuthorizationFeature {
|
||||
|
||||
public static final String NAME = "canManageRelationships";
|
||||
|
||||
@Autowired
|
||||
private AuthorizeService authorizeService;
|
||||
|
||||
@Autowired
|
||||
private Utils utils;
|
||||
|
||||
@Override
|
||||
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
|
||||
if (object instanceof ItemRest) {
|
||||
DSpaceObject dSpaceObject = (DSpaceObject) utils.getDSpaceAPIObjectFromRest(context, object);
|
||||
return authorizeService.authorizeActionBoolean(context, context.getCurrentUser(),
|
||||
dSpaceObject, Constants.WRITE, true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedTypes() {
|
||||
return new String[]{
|
||||
ItemRest.CATEGORY + "." + ItemRest.NAME
|
||||
};
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.rest.authorization.impl;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.dspace.app.rest.authorization.AuthorizationFeature;
|
||||
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
|
||||
import org.dspace.app.rest.model.BaseObjectRest;
|
||||
import org.dspace.app.rest.model.ItemRest;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* The manage versions feature. It can be used to verify
|
||||
* if the user can create/delete or update the version of an Item.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
|
||||
*/
|
||||
@Component
|
||||
@AuthorizationFeatureDocumentation(name = CanManageVersionsFeature.NAME,
|
||||
description = "It can be used to verify if the user can create/delete or update the version of an Item")
|
||||
public class CanManageVersionsFeature implements AuthorizationFeature {
|
||||
|
||||
public static final String NAME = "canManageVersions";
|
||||
|
||||
@Autowired
|
||||
private ConfigurationService configurationService;
|
||||
|
||||
@Autowired
|
||||
private AuthorizeService authorizeService;
|
||||
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
|
||||
@Override
|
||||
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
|
||||
if (object instanceof ItemRest) {
|
||||
EPerson currentUser = context.getCurrentUser();
|
||||
if (Objects.isNull(currentUser)) {
|
||||
return false;
|
||||
}
|
||||
if (authorizeService.isAdmin(context)) {
|
||||
return true;
|
||||
}
|
||||
if (configurationService.getBooleanProperty("versioning.submitterCanCreateNewVersion")) {
|
||||
Item item = itemService.find(context, UUID.fromString(((ItemRest) object).getUuid()));
|
||||
EPerson submitter = item.getSubmitter();
|
||||
return Objects.nonNull(submitter) && currentUser.getID().equals(submitter.getID());
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedTypes() {
|
||||
return new String[]{
|
||||
ItemRest.CATEGORY + "." + ItemRest.NAME
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@@ -27,11 +27,11 @@ import org.springframework.stereotype.Component;
|
||||
* current user is the object's admin. Otherwise, authorization is granted if the current user can view the object.
|
||||
*/
|
||||
@Component
|
||||
@AuthorizationFeatureDocumentation(name = ViewVersionsFeature.NAME,
|
||||
@AuthorizationFeatureDocumentation(name = CanSeeVersionsFeature.NAME,
|
||||
description = "It can be used to verify if the user can view the versions of an Item")
|
||||
public class ViewVersionsFeature implements AuthorizationFeature {
|
||||
public class CanSeeVersionsFeature implements AuthorizationFeature {
|
||||
|
||||
public final static String NAME = "canViewVersions";
|
||||
public final static String NAME = "canSeeVersions";
|
||||
|
||||
@Autowired
|
||||
private ConfigurationService configurationService;
|
@@ -11,6 +11,7 @@ import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.dspace.app.rest.model.SearchConfigurationRest;
|
||||
import org.dspace.app.rest.projection.Projection;
|
||||
import org.dspace.discovery.configuration.DiscoveryConfiguration;
|
||||
@@ -36,7 +37,6 @@ public class DiscoverConfigurationConverter
|
||||
addSearchFilters(searchConfigurationRest,
|
||||
configuration.getSearchFilters(), configuration.getSidebarFacets());
|
||||
addSortOptions(searchConfigurationRest, configuration.getSearchSortConfiguration());
|
||||
setDefaultSortOption(configuration, searchConfigurationRest);
|
||||
}
|
||||
return searchConfigurationRest;
|
||||
}
|
||||
@@ -46,23 +46,6 @@ public class DiscoverConfigurationConverter
|
||||
return DiscoveryConfiguration.class;
|
||||
}
|
||||
|
||||
private void setDefaultSortOption(DiscoveryConfiguration configuration,
|
||||
SearchConfigurationRest searchConfigurationRest) {
|
||||
String defaultSort = configuration.getSearchSortConfiguration().SCORE;
|
||||
if (configuration.getSearchSortConfiguration() != null) {
|
||||
DiscoverySortFieldConfiguration discoverySortFieldConfiguration = configuration.getSearchSortConfiguration()
|
||||
.getSortFieldConfiguration(
|
||||
defaultSort);
|
||||
if (discoverySortFieldConfiguration != null) {
|
||||
SearchConfigurationRest.SortOption sortOption = new SearchConfigurationRest.SortOption();
|
||||
sortOption.setName(discoverySortFieldConfiguration.getMetadataField());
|
||||
sortOption.setActualName(discoverySortFieldConfiguration.getType());
|
||||
searchConfigurationRest.addSortOption(sortOption);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void addSearchFilters(SearchConfigurationRest searchConfigurationRest,
|
||||
List<DiscoverySearchFilter> searchFilterList,
|
||||
List<DiscoverySearchFilterFacet> facetList) {
|
||||
@@ -88,8 +71,13 @@ public class DiscoverConfigurationConverter
|
||||
for (DiscoverySortFieldConfiguration discoverySearchSortConfiguration : CollectionUtils
|
||||
.emptyIfNull(searchSortConfiguration.getSortFields())) {
|
||||
SearchConfigurationRest.SortOption sortOption = new SearchConfigurationRest.SortOption();
|
||||
sortOption.setName(discoverySearchSortConfiguration.getMetadataField());
|
||||
if (StringUtils.isBlank(discoverySearchSortConfiguration.getMetadataField())) {
|
||||
sortOption.setName(DiscoverySortConfiguration.SCORE);
|
||||
} else {
|
||||
sortOption.setName(discoverySearchSortConfiguration.getMetadataField());
|
||||
}
|
||||
sortOption.setActualName(discoverySearchSortConfiguration.getType());
|
||||
sortOption.setSortOrder(discoverySearchSortConfiguration.getDefaultSortOrder().name());
|
||||
searchConfigurationRest.addSortOption(sortOption);
|
||||
}
|
||||
}
|
||||
|
@@ -11,18 +11,17 @@ import static org.springframework.web.servlet.DispatcherServlet.EXCEPTION_ATTRIB
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.rest.security.RestAuthenticationService;
|
||||
import org.dspace.app.rest.utils.ContextUtil;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.core.Context;
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.repository.support.QueryMethodParameterConversionException;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -59,13 +58,11 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH
|
||||
*/
|
||||
private static final Set<Integer> LOG_AS_ERROR = Set.of(422);
|
||||
|
||||
@Autowired
|
||||
private RestAuthenticationService restAuthenticationService;
|
||||
|
||||
@ExceptionHandler({AuthorizeException.class, RESTAuthorizationException.class, AccessDeniedException.class})
|
||||
protected void handleAuthorizeException(HttpServletRequest request, HttpServletResponse response, Exception ex)
|
||||
throws IOException {
|
||||
if (restAuthenticationService.hasAuthenticationData(request)) {
|
||||
Context context = ContextUtil.obtainContext(request);
|
||||
if (Objects.nonNull(context.getCurrentUser())) {
|
||||
sendErrorResponse(request, response, ex, "Access is denied", HttpServletResponse.SC_FORBIDDEN);
|
||||
} else {
|
||||
sendErrorResponse(request, response, ex, "Authentication is required", HttpServletResponse.SC_UNAUTHORIZED);
|
||||
@@ -119,6 +116,14 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH
|
||||
HttpStatus.UNPROCESSABLE_ENTITY.value());
|
||||
}
|
||||
|
||||
@ExceptionHandler( {InvalidSearchRequestException.class})
|
||||
protected void handleInvalidSearchRequestException(HttpServletRequest request, HttpServletResponse response,
|
||||
Exception ex) throws IOException {
|
||||
sendErrorResponse(request, response, null,
|
||||
"Invalid search request",
|
||||
HttpStatus.UNPROCESSABLE_ENTITY.value());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add user-friendly error messages to the response body for selected errors.
|
||||
* Since the error messages will be exposed to the API user, the exception classes are expected to implement
|
||||
|
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.rest.exception;
|
||||
|
||||
import org.dspace.app.rest.utils.DiscoverQueryBuilder;
|
||||
|
||||
/**
|
||||
* This exception is thrown when the given search configuration
|
||||
* passed to {@link DiscoverQueryBuilder} is invalid
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
|
||||
*/
|
||||
public class InvalidSearchRequestException extends RuntimeException {
|
||||
|
||||
public InvalidSearchRequestException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public InvalidSearchRequestException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
@@ -247,6 +247,7 @@ public class SearchConfigurationRest extends BaseObjectRest<String> {
|
||||
@JsonIgnore
|
||||
private String actualName;
|
||||
private String name;
|
||||
private String sortOrder;
|
||||
|
||||
public void setActualName(String name) {
|
||||
this.actualName = name;
|
||||
@@ -264,6 +265,14 @@ public class SearchConfigurationRest extends BaseObjectRest<String> {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getSortOrder() {
|
||||
return sortOrder;
|
||||
}
|
||||
|
||||
public void setSortOrder(String sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
return (object instanceof SearchConfigurationRest.SortOption &&
|
||||
|
@@ -12,6 +12,7 @@ import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.dspace.app.rest.utils.URLUtils;
|
||||
import org.dspace.app.rest.utils.Utils;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
@@ -102,6 +103,8 @@ public class EmbeddedPageHeader {
|
||||
if (page != null) {
|
||||
// replace existing page & size params (if exist), otherwise append them
|
||||
uriComp = uriComp.replaceQueryParam("page", page);
|
||||
}
|
||||
if (size != Utils.DEFAULT_PAGE_SIZE) {
|
||||
uriComp = uriComp.replaceQueryParam("size", size);
|
||||
}
|
||||
return new Href(uriComp.build().toUriString());
|
||||
|
@@ -190,22 +190,22 @@ public class MetadataFieldRestRepository extends DSpaceRestRepository<MetadataFi
|
||||
"forming schema.element.qualifier metadata field name");
|
||||
}
|
||||
filterQueries.add(searchService.toFilterQuery(context, MetadataFieldIndexFactoryImpl.FIELD_NAME_VARIATIONS,
|
||||
OPERATOR_EQUALS, query).getFilterQuery() + "*");
|
||||
OPERATOR_EQUALS, query, null).getFilterQuery() + "*");
|
||||
}
|
||||
if (StringUtils.isNotBlank(schemaName)) {
|
||||
filterQueries.add(
|
||||
searchService.toFilterQuery(context, MetadataFieldIndexFactoryImpl.SCHEMA_FIELD_NAME, OPERATOR_EQUALS,
|
||||
schemaName).getFilterQuery());
|
||||
schemaName, null).getFilterQuery());
|
||||
}
|
||||
if (StringUtils.isNotBlank(elementName)) {
|
||||
filterQueries.add(
|
||||
searchService.toFilterQuery(context, MetadataFieldIndexFactoryImpl.ELEMENT_FIELD_NAME, OPERATOR_EQUALS,
|
||||
elementName).getFilterQuery());
|
||||
elementName, null).getFilterQuery());
|
||||
}
|
||||
if (StringUtils.isNotBlank(qualifierName)) {
|
||||
filterQueries.add(searchService
|
||||
.toFilterQuery(context, MetadataFieldIndexFactoryImpl.QUALIFIER_FIELD_NAME, OPERATOR_EQUALS,
|
||||
qualifierName).getFilterQuery());
|
||||
qualifierName, null).getFilterQuery());
|
||||
}
|
||||
|
||||
DiscoverQuery discoverQuery = new DiscoverQuery();
|
||||
|
@@ -53,10 +53,11 @@ public interface RestAuthenticationService {
|
||||
* Checks the current request for a valid authentication token. If found, extracts that token and obtains the
|
||||
* currently logged in EPerson.
|
||||
* @param request current request
|
||||
* @param request current response
|
||||
* @param context current DSpace Context
|
||||
* @return EPerson of the logged in user (if auth token found), or null if no auth token is found
|
||||
*/
|
||||
EPerson getAuthenticatedEPerson(HttpServletRequest request, Context context);
|
||||
EPerson getAuthenticatedEPerson(HttpServletRequest request, HttpServletResponse response, Context context);
|
||||
|
||||
/**
|
||||
* Checks the current request for a valid authentication token. If found, returns true. If not found, returns false
|
||||
@@ -70,8 +71,7 @@ public interface RestAuthenticationService {
|
||||
* existing authentication data/token is destroyed/invalidated and cannot be reused in later requests.
|
||||
* <P>
|
||||
* In other words, this method invalidates the authentication data created by addAuthenticationDataForUser().
|
||||
* This also should include clearing any Cookie created by that method, usually by calling the separate
|
||||
* invalidateAuthenticationCookie() method in this same class.
|
||||
*
|
||||
* @param request current request
|
||||
* @param response current response
|
||||
* @param context current DSpace Context.
|
||||
@@ -102,8 +102,9 @@ public interface RestAuthenticationService {
|
||||
* addAuthenticationDataForUser()). It's useful for those services to immediately *remove/discard* the Cookie after
|
||||
* it has been used. This ensures the auth Cookie is temporary in nature, and is destroyed as soon as it is no
|
||||
* longer needed.
|
||||
* @param request current request
|
||||
* @param res current response (where Cookie should be destroyed)
|
||||
*/
|
||||
void invalidateAuthenticationCookie(HttpServletResponse res);
|
||||
void invalidateAuthenticationCookie(HttpServletRequest request, HttpServletResponse res);
|
||||
|
||||
}
|
||||
|
@@ -14,14 +14,21 @@ import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.dspace.authenticate.ShibAuthentication;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.ProviderNotFoundException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
/**
|
||||
* This class will filter shibboleth requests to try and authenticate them
|
||||
* This class will filter Shibboleth requests to see if the user has been authenticated via Shibboleth.
|
||||
* <P>
|
||||
* This filter runs before the ShibbolethRestController, in order to verify Shibboleth authentication succeeded,
|
||||
* and create the authentication token (JWT).
|
||||
*
|
||||
* @author Giuseppe Digilio (giuseppe dot digilio at 4science dot it)
|
||||
* @see org.dspace.app.rest.ShibbolethRestController
|
||||
* @see org.dspace.authenticate.ShibAuthentication
|
||||
*/
|
||||
public class ShibbolethAuthenticationFilter extends StatelessLoginFilter {
|
||||
|
||||
@@ -33,7 +40,16 @@ public class ShibbolethAuthenticationFilter extends StatelessLoginFilter {
|
||||
@Override
|
||||
public Authentication attemptAuthentication(HttpServletRequest req,
|
||||
HttpServletResponse res) throws AuthenticationException {
|
||||
// First, if Shibboleth is not enabled, throw an immediate ProviderNotFoundException
|
||||
// This tells Spring Security that authentication failed
|
||||
if (!ShibAuthentication.isEnabled()) {
|
||||
throw new ProviderNotFoundException("Shibboleth is disabled.");
|
||||
}
|
||||
|
||||
// In the case of Shibboleth, this method does NOT actually authenticate us. The authentication
|
||||
// has already happened in Shibboleth & we are just intercepting the return request in order to check
|
||||
// for a valid Shibboleth login (using ShibAuthentication.authenticate()) & save current user to Context
|
||||
// See org.dspace.app.rest.ShibbolethRestController JavaDocs for an outline of the entire Shib login process.
|
||||
return authenticationManager.authenticate(
|
||||
new DSpaceAuthentication(null, null, new ArrayList<>())
|
||||
);
|
||||
@@ -44,8 +60,14 @@ public class ShibbolethAuthenticationFilter extends StatelessLoginFilter {
|
||||
HttpServletResponse res,
|
||||
FilterChain chain,
|
||||
Authentication auth) throws IOException, ServletException {
|
||||
// Once we've gotten here, we know we have a successful login (i.e. attemptAuthentication() succeeded)
|
||||
|
||||
DSpaceAuthentication dSpaceAuthentication = (DSpaceAuthentication) auth;
|
||||
// OVERRIDE DEFAULT behavior of StatelessLoginFilter to return a temporary authentication cookie containing
|
||||
// the Auth Token (JWT). This Cookie is required because ShibbolethRestController *redirects* the user
|
||||
// back to the client/UI after a successful Shibboleth login. Headers cannot be sent via a redirect, so a Cookie
|
||||
// must be sent to provide the auth token to the client. On the next request from the client, the cookie is
|
||||
// read and destroyed & the Auth token is only used in the Header from that point forward.
|
||||
restAuthenticationService.addAuthenticationDataForUser(req, res, dSpaceAuthentication, true);
|
||||
chain.doFilter(req, res);
|
||||
}
|
||||
|
@@ -39,7 +39,8 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFi
|
||||
|
||||
/**
|
||||
* Custom Spring authentication filter for Stateless authentication, intercepts requests to check for valid
|
||||
* authentication
|
||||
* authentication. This runs before *every* request in the DSpace backend to see if any authentication data
|
||||
* is passed in that request. If so, it authenticates the EPerson in the current Context.
|
||||
*
|
||||
* @author Frederic Van Reet (frederic dot vanreet at atmire dot com)
|
||||
* @author Tom Desair (tom dot desair at atmire dot com)
|
||||
@@ -94,9 +95,9 @@ public class StatelessAuthenticationFilter extends BasicAuthenticationFilter {
|
||||
log.error("Access is denied (status:{})", HttpServletResponse.SC_FORBIDDEN, e);
|
||||
return;
|
||||
}
|
||||
// If we have a valid Authentication, save it to Spring Security
|
||||
if (authentication != null) {
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
restAuthenticationService.invalidateAuthenticationCookie(res);
|
||||
}
|
||||
chain.doFilter(req, res);
|
||||
}
|
||||
@@ -123,7 +124,7 @@ public class StatelessAuthenticationFilter extends BasicAuthenticationFilter {
|
||||
|
||||
Context context = ContextUtil.obtainContext(request);
|
||||
|
||||
EPerson eperson = restAuthenticationService.getAuthenticatedEPerson(request, context);
|
||||
EPerson eperson = restAuthenticationService.getAuthenticatedEPerson(request, res, context);
|
||||
if (eperson != null) {
|
||||
//Pass the eperson ID to the request service
|
||||
requestService.setCurrentUserId(eperson.getID());
|
||||
|
@@ -23,7 +23,10 @@ import org.springframework.security.web.authentication.AbstractAuthenticationPro
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
/**
|
||||
* This class will filter login requests to try and authenticate them
|
||||
* This class will filter /api/authn/login requests to try and authenticate them. Keep in mind, this filter runs *after*
|
||||
* StatelessAuthenticationFilter (which looks for authentication data in the request itself). So, in some scenarios
|
||||
* (e.g. after a Shibboleth login) the StatelessAuthenticationFilter does the actual authentication, and this Filter
|
||||
* just ensures the auth token (JWT) is sent back in an Authorization header.
|
||||
*
|
||||
* @author Frederic Van Reet (frederic dot vanreet at atmire dot com)
|
||||
* @author Tom Desair (tom dot desair at atmire dot com)
|
||||
@@ -46,6 +49,19 @@ public class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter
|
||||
this.restAuthenticationService = restAuthenticationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to authenticate the user by using Spring Security's AuthenticationManager.
|
||||
* The AuthenticationManager will delegate this task to one or more AuthenticationProvider classes.
|
||||
* <P>
|
||||
* For DSpace, our custom AuthenticationProvider is {@link EPersonRestAuthenticationProvider}, so that
|
||||
* is the authenticate() method which is called below.
|
||||
*
|
||||
* @param req current request
|
||||
* @param res current response
|
||||
* @return a valid Spring Security Authentication object if authentication succeeds
|
||||
* @throws AuthenticationException if authentication fails
|
||||
* @see EPersonRestAuthenticationProvider
|
||||
*/
|
||||
@Override
|
||||
public Authentication attemptAuthentication(HttpServletRequest req,
|
||||
HttpServletResponse res) throws AuthenticationException {
|
||||
@@ -53,12 +69,28 @@ public class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter
|
||||
String user = req.getParameter("user");
|
||||
String password = req.getParameter("password");
|
||||
|
||||
// Attempt to authenticate by passing user & password (if provided) to AuthenticationProvider class(es)
|
||||
return authenticationManager.authenticate(
|
||||
new DSpaceAuthentication(user, password, new ArrayList<>())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the above attemptAuthentication() call was successful (no authentication error was thrown),
|
||||
* then this method will take the returned {@link DSpaceAuthentication} class (which includes all
|
||||
* the data from the authenticated user) and add the authentication data to the response.
|
||||
* <P>
|
||||
* For DSpace, this is calling our {@link org.dspace.app.rest.security.jwt.JWTTokenRestAuthenticationServiceImpl}
|
||||
* in order to create a JWT based on the authentication data & send that JWT back in the response.
|
||||
*
|
||||
* @param req current request
|
||||
* @param res response
|
||||
* @param chain FilterChain
|
||||
* @param auth Authentication object containing info about user who had a successful authentication
|
||||
* @throws IOException
|
||||
* @throws ServletException
|
||||
* @see org.dspace.app.rest.security.jwt.JWTTokenRestAuthenticationServiceImpl
|
||||
*/
|
||||
@Override
|
||||
protected void successfulAuthentication(HttpServletRequest req,
|
||||
HttpServletResponse res,
|
||||
@@ -69,6 +101,16 @@ public class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter
|
||||
restAuthenticationService.addAuthenticationDataForUser(req, res, dSpaceAuthentication, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the above attemptAuthentication() call was unsuccessful, then ensure that the response is a 401 Unauthorized
|
||||
* AND it includes a WWW-Authentication header. We use this header in DSpace to return all the enabled
|
||||
* authentication options available to the UI (along with the path to the login URL for each option)
|
||||
* @param request current request
|
||||
* @param response current response
|
||||
* @param failed exception that was thrown by attemptAuthentication()
|
||||
* @throws IOException
|
||||
* @throws ServletException
|
||||
*/
|
||||
@Override
|
||||
protected void unsuccessfulAuthentication(HttpServletRequest request,
|
||||
HttpServletResponse response, AuthenticationException failed)
|
||||
|
@@ -89,10 +89,7 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
||||
context.commit();
|
||||
|
||||
// Add newly generated auth token to the response
|
||||
addTokenToResponse(response, token, addCookie);
|
||||
|
||||
// Reset our CSRF token, generating a new one
|
||||
resetCSRFToken(request, response);
|
||||
addTokenToResponse(request, response, token, addCookie);
|
||||
|
||||
} catch (JOSEException e) {
|
||||
log.error("JOSE Exception", e);
|
||||
@@ -125,9 +122,9 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
||||
}
|
||||
|
||||
@Override
|
||||
public EPerson getAuthenticatedEPerson(HttpServletRequest request, Context context) {
|
||||
public EPerson getAuthenticatedEPerson(HttpServletRequest request, HttpServletResponse response, Context context) {
|
||||
try {
|
||||
String token = getLoginToken(request);
|
||||
String token = getLoginToken(request, response);
|
||||
EPerson ePerson = null;
|
||||
if (token == null) {
|
||||
token = getShortLivedToken(request);
|
||||
@@ -156,22 +153,29 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
||||
@Override
|
||||
public void invalidateAuthenticationData(HttpServletRequest request, HttpServletResponse response,
|
||||
Context context) throws Exception {
|
||||
String token = getLoginToken(request);
|
||||
invalidateAuthenticationCookie(response);
|
||||
String token = getLoginToken(request, response);
|
||||
loginJWTTokenHandler.invalidateToken(token, request, context);
|
||||
|
||||
// Reset our CSRF token, generating a new one
|
||||
resetCSRFToken(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate our temporary authentication cookie by overwriting it in the response.
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
@Override
|
||||
public void invalidateAuthenticationCookie(HttpServletResponse response) {
|
||||
public void invalidateAuthenticationCookie(HttpServletRequest request, HttpServletResponse response) {
|
||||
// Re-send the same cookie (as addTokenToResponse()) with no value and a Max-Age of 0 seconds
|
||||
ResponseCookie cookie = ResponseCookie.from(AUTHORIZATION_COOKIE, "")
|
||||
.maxAge(0).httpOnly(true).secure(true).sameSite("None").build();
|
||||
|
||||
// Write the cookie to the Set-Cookie header in order to send it
|
||||
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
||||
|
||||
// Reset our CSRF token, generating a new one
|
||||
resetCSRFToken(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -179,6 +183,18 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
||||
return authenticationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a comma-separated list of all currently enabled authentication options (based on DSpace configuration).
|
||||
* This list is sent to the client in the WWW-Authenticate header in order to inform it of all the enabled
|
||||
* authentication plugins *and* (optionally) to provide it with the "location" of the login page, if
|
||||
* the authentication plugin requires an external login page (e.g. Shibboleth).
|
||||
* <P>
|
||||
* Example output looks like:
|
||||
* shibboleth realm="DSpace REST API" location=[shibboleth-url], password realm="DSpace REST API"
|
||||
* @param request The current client request
|
||||
* @param response The response being build for the client
|
||||
* @return comma separated list of authentication options
|
||||
*/
|
||||
@Override
|
||||
public String getWwwAuthenticateHeaderValue(final HttpServletRequest request, final HttpServletResponse response) {
|
||||
Iterator<AuthenticationMethod> authenticationMethodIterator
|
||||
@@ -195,10 +211,12 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
||||
|
||||
wwwAuthenticate.append(authenticationMethod.getName()).append(" realm=\"DSpace REST API\"");
|
||||
|
||||
// If authentication method requires a custom login page, add that as the "location". The client is
|
||||
// expected to read this "location" and send users to that URL when this authentication option is selected
|
||||
// We cannot reply with a 303 code because many browsers handle 3xx response codes transparently. This
|
||||
// means that the JavaScript client code is not aware of the 303 status and fails to react accordingly.
|
||||
String loginPageURL = authenticationMethod.loginPageURL(context, request, response);
|
||||
if (org.apache.commons.lang3.StringUtils.isNotBlank(loginPageURL)) {
|
||||
// We cannot reply with a 303 code because may browsers handle 3xx response codes transparently. This
|
||||
// means that the JavaScript client code is not aware of the 303 status and fails to react accordingly.
|
||||
wwwAuthenticate.append(", location=\"").append(loginPageURL).append("\"");
|
||||
}
|
||||
}
|
||||
@@ -207,32 +225,57 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Authentication token (JWT) to the response either in a header (default) or in a cookie.
|
||||
* Adds the Authentication token (JWT) to the response either in a header (default) or in a temporary cookie.
|
||||
* <P>
|
||||
* If 'addCookie' is true, then the JWT is also added to a response cookie. This is primarily for support of auth
|
||||
* plugins which _require_ cookie-based auth (e.g. Shibboleth). Note that this cookie can be used cross-site
|
||||
* (i.e. SameSite=None), but cannot be used by Javascript (HttpOnly) including the Angular UI. It also will only be
|
||||
* (i.e. SameSite=None), but cannot be used by Javascript (HttpOnly), including the Angular UI. It also will only be
|
||||
* sent via HTTPS (Secure).
|
||||
* <P>
|
||||
* If 'addCookie' is false, then the JWT is only added in the Authorization header. This is recommended behavior
|
||||
* as it is the most secure. For the UI (or any JS clients) the JWT must be sent in the Authorization header.
|
||||
* If 'addCookie' is false, then the JWT is only added in the Authorization header & the auth cookie (if it exists)
|
||||
* is removed. This ensures we are primarily using the Authorization header & remove the temporary auth cookie as
|
||||
* soon as it is no longer needed.
|
||||
* <P>
|
||||
* Because this method is called for login actions, it usually resets the CSRF token, *except* when the auth cookie
|
||||
* is being created. This is because we will reset the CSRF token once the auth cookie is used & invalidated.
|
||||
* @param request current request
|
||||
* @param response current response
|
||||
* @param token the authentication token
|
||||
* @param addCookie whether to send token in a cookie (true) or header (false)
|
||||
* @param addCookie whether to send token in a cookie & header (true) or header only (false)
|
||||
*/
|
||||
private void addTokenToResponse(final HttpServletResponse response, final String token, final Boolean addCookie) {
|
||||
// we need authentication cookies because Shibboleth can't use the authentication headers due to the redirects
|
||||
private void addTokenToResponse(final HttpServletRequest request, final HttpServletResponse response,
|
||||
final String token, final Boolean addCookie) {
|
||||
// If addCookie=true, create a temporary authentication cookie. This is primarily used for the initial
|
||||
// Shibboleth response (which requires a number of redirects), as headers cannot be sent via a redirect. As soon
|
||||
// as the UI (or Hal Browser) obtains the Shibboleth login data, it makes a call to /login (addCookie=false)
|
||||
// which destroys this temporary auth cookie. So, the auth cookie only exists a few seconds.
|
||||
if (addCookie) {
|
||||
ResponseCookie cookie = ResponseCookie.from(AUTHORIZATION_COOKIE, token)
|
||||
.httpOnly(true).secure(true).sameSite("None").build();
|
||||
|
||||
// Write the cookie to the Set-Cookie header in order to send it
|
||||
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
||||
// NOTE: Because the auth cookie is meant to be temporary, we do NOT reset our CSRF token when creating it.
|
||||
// Instead, we'll reset the CSRF token when the auth cookie is *destroyed* during call to /login.
|
||||
} else if (hasAuthorizationCookie(request)) {
|
||||
// Since an auth cookie exists & is no longer needed (addCookie=false), remove/invalidate the auth cookie.
|
||||
// This also resets the CSRF token, as auth cookie is destroyed when /login is called.
|
||||
invalidateAuthenticationCookie(request, response);
|
||||
} else {
|
||||
// If we are just adding a new token to header, then reset the CSRF token.
|
||||
// This forces the token to change when login process doesn't rely on auth cookie.
|
||||
resetCSRFToken(request, response);
|
||||
}
|
||||
response.setHeader(AUTHORIZATION_HEADER, String.format("%s %s", AUTHORIZATION_TYPE, token));
|
||||
}
|
||||
|
||||
private String getLoginToken(HttpServletRequest request) {
|
||||
/**
|
||||
* Get the Login token (JWT) in the current request. First we check the Authorization header.
|
||||
* If not found there, we check for a temporary authentication cookie and use that.
|
||||
* @param request current request
|
||||
* @return authentication token (if found), or null
|
||||
*/
|
||||
private String getLoginToken(HttpServletRequest request, HttpServletResponse response) {
|
||||
String tokenValue = null;
|
||||
String authHeader = request.getHeader(AUTHORIZATION_HEADER);
|
||||
String authCookie = getAuthorizationCookie(request);
|
||||
@@ -254,6 +297,11 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
||||
return tokenValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the (temporary) authorization cookie, if exists.
|
||||
* @param request current request
|
||||
* @return string cookie value
|
||||
*/
|
||||
private String getAuthorizationCookie(HttpServletRequest request) {
|
||||
String authCookie = "";
|
||||
Cookie[] cookies = request.getCookies();
|
||||
@@ -261,12 +309,26 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
||||
for (Cookie cookie : cookies) {
|
||||
if (cookie.getName().equals(AUTHORIZATION_COOKIE) && StringUtils.isNotEmpty(cookie.getValue())) {
|
||||
authCookie = cookie.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return authCookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the (temporary) authorization cookie exists and is not empty.
|
||||
* @param request current request
|
||||
* @return true if cookie is found in request. false otherwise.
|
||||
*/
|
||||
private boolean hasAuthorizationCookie(HttpServletRequest request) {
|
||||
if (StringUtils.isNotEmpty(getAuthorizationCookie(request))) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force reset the CSRF Token, causing a new one to be generated.
|
||||
* This method is used internally during login/logout to ensure a new CSRF token is generated anytime authentication
|
||||
|
@@ -8,16 +8,28 @@
|
||||
package org.dspace.app.rest.submit.factory.impl;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
||||
import org.dspace.app.rest.model.MetadataValueRest;
|
||||
import org.dspace.app.rest.model.patch.LateObjectEvaluator;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.InProgressSubmission;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.MetadataValue;
|
||||
import org.dspace.content.Relationship;
|
||||
import org.dspace.content.RelationshipMetadataValue;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.content.service.RelationshipService;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.Utils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -66,9 +78,18 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class ItemMetadataValueAddPatchOperation extends MetadataValueAddPatchOperation<Item> {
|
||||
|
||||
/**
|
||||
* log4j category
|
||||
*/
|
||||
private static final Logger log =
|
||||
org.apache.logging.log4j.LogManager.getLogger(ItemMetadataValueAddPatchOperation.class);
|
||||
|
||||
@Autowired
|
||||
ItemService itemService;
|
||||
|
||||
@Autowired
|
||||
RelationshipService relationshipService;
|
||||
|
||||
@Override
|
||||
void add(Context context, HttpServletRequest currentRequest, InProgressSubmission source, String path, Object value)
|
||||
throws SQLException {
|
||||
@@ -109,6 +130,102 @@ public class ItemMetadataValueAddPatchOperation extends MetadataValueAddPatchOpe
|
||||
|
||||
}
|
||||
|
||||
protected void replaceValue(Context context, Item source, String target, List<MetadataValueRest> list)
|
||||
throws SQLException {
|
||||
String[] metadata = Utils.tokenize(target);
|
||||
|
||||
// fetch pre-existent metadata
|
||||
List<MetadataValue> preExistentMetadata =
|
||||
getDSpaceObjectService().getMetadata(source, metadata[0], metadata[1], metadata[2], Item.ANY);
|
||||
|
||||
// fetch pre-existent relationships
|
||||
Map<Integer, Relationship> preExistentRelationships = preExistentRelationships(context, preExistentMetadata);
|
||||
|
||||
// clear all plain metadata
|
||||
getDSpaceObjectService().clearMetadata(context, source, metadata[0], metadata[1], metadata[2], Item.ANY);
|
||||
// remove all deleted relationships
|
||||
for (Relationship rel : preExistentRelationships.values()) {
|
||||
try {
|
||||
Optional<MetadataValueRest> stillPresent = list.stream()
|
||||
.filter(ll -> ll.getAuthority() != null && rel.getID().equals(getRelId(ll.getAuthority())))
|
||||
.findAny();
|
||||
if (stillPresent.isEmpty()) {
|
||||
relationshipService.delete(context, rel);
|
||||
}
|
||||
} catch (AuthorizeException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Authorize Exception during relationship deletion.");
|
||||
}
|
||||
}
|
||||
|
||||
// create plain metadata / move relationships in the list order
|
||||
|
||||
// if a virtual value is present in the list, it must be present in preExistentRelationships too.
|
||||
// (with this operator virtual value can only be moved or deleted).
|
||||
int idx = 0;
|
||||
for (MetadataValueRest ll : list) {
|
||||
if (StringUtils.startsWith(ll.getAuthority(), Constants.VIRTUAL_AUTHORITY_PREFIX)) {
|
||||
|
||||
Optional<MetadataValue> preExistentMv = preExistentMetadata.stream().filter(mvr ->
|
||||
StringUtils.equals(ll.getAuthority(), mvr.getAuthority())).findFirst();
|
||||
|
||||
if (!preExistentMv.isPresent()) {
|
||||
throw new UnprocessableEntityException(
|
||||
"Relationship with authority=" + ll.getAuthority() + " not found");
|
||||
}
|
||||
|
||||
final RelationshipMetadataValue rmv = (RelationshipMetadataValue) preExistentMv.get();
|
||||
final Relationship rel = preExistentRelationships.get(rmv.getRelationshipId());
|
||||
this.updateRelationshipPlace(context, source, idx, rel);
|
||||
|
||||
} else {
|
||||
getDSpaceObjectService()
|
||||
.addMetadata(context, source, metadata[0], metadata[1], metadata[2],
|
||||
ll.getLanguage(), ll.getValue(), ll.getAuthority(), ll.getConfidence(), idx);
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve Relationship Objects from a List of MetadataValue.
|
||||
*/
|
||||
private Map<Integer, Relationship> preExistentRelationships(Context context,
|
||||
List<MetadataValue> preExistentMetadata) throws SQLException {
|
||||
Map<Integer, Relationship> relationshipsMap = new HashMap<Integer, Relationship>();
|
||||
for (MetadataValue ll : preExistentMetadata) {
|
||||
if (ll instanceof RelationshipMetadataValue) {
|
||||
Relationship relationship = relationshipService
|
||||
.find(context, ((RelationshipMetadataValue) ll).getRelationshipId());
|
||||
if (relationship != null) {
|
||||
relationshipsMap.put(relationship.getID(), relationship);
|
||||
}
|
||||
}
|
||||
}
|
||||
return relationshipsMap;
|
||||
}
|
||||
|
||||
private Integer getRelId(String authority) {
|
||||
final int relId = Integer.parseInt(authority.split(Constants.VIRTUAL_AUTHORITY_PREFIX)[1]);
|
||||
return relId;
|
||||
}
|
||||
|
||||
private void updateRelationshipPlace(Context context, Item dso, int place, Relationship rs) {
|
||||
|
||||
try {
|
||||
if (rs.getLeftItem() == dso) {
|
||||
rs.setLeftPlace(place);
|
||||
} else {
|
||||
rs.setRightPlace(place);
|
||||
}
|
||||
relationshipService.update(context, rs);
|
||||
} catch (Exception e) {
|
||||
//should not occur, otherwise metadata can't be updated either
|
||||
log.error("An error occurred while moving " + rs.getID() + " for item " + dso.getID(), e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ItemService getDSpaceObjectService() {
|
||||
return itemService;
|
||||
|
@@ -14,12 +14,14 @@ import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.rest.converter.query.SearchQueryConverter;
|
||||
import org.dspace.app.rest.exception.DSpaceBadRequestException;
|
||||
import org.dspace.app.rest.exception.InvalidSearchRequestException;
|
||||
import org.dspace.app.rest.parameter.SearchFilter;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.LogManager;
|
||||
@@ -292,6 +294,11 @@ public class DiscoverQueryBuilder implements InitializingBean {
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(sortBy) && !isConfigured(sortBy, searchSortConfiguration)) {
|
||||
throw new InvalidSearchRequestException(
|
||||
"The field: " + sortBy + "is not configured for the configuration!");
|
||||
}
|
||||
|
||||
//Load defaults if we did not receive values
|
||||
if (sortBy == null) {
|
||||
sortBy = getDefaultSortField(searchSortConfiguration);
|
||||
@@ -321,10 +328,14 @@ public class DiscoverQueryBuilder implements InitializingBean {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isConfigured(String sortBy, DiscoverySortConfiguration searchSortConfiguration) {
|
||||
return Objects.nonNull(searchSortConfiguration.getSortFieldConfiguration(sortBy));
|
||||
}
|
||||
|
||||
private String getDefaultSortDirection(DiscoverySortConfiguration searchSortConfiguration, String sortOrder) {
|
||||
if (searchSortConfiguration != null) {
|
||||
sortOrder = searchSortConfiguration.getDefaultSortOrder()
|
||||
.toString();
|
||||
if (Objects.nonNull(searchSortConfiguration.getSortFields()) &&
|
||||
!searchSortConfiguration.getSortFields().isEmpty()) {
|
||||
sortOrder = searchSortConfiguration.getSortFields().get(0).getDefaultSortOrder().name();
|
||||
}
|
||||
return sortOrder;
|
||||
}
|
||||
@@ -332,8 +343,12 @@ public class DiscoverQueryBuilder implements InitializingBean {
|
||||
private String getDefaultSortField(DiscoverySortConfiguration searchSortConfiguration) {
|
||||
String sortBy;// Attempt to find the default one, if none found we use SCORE
|
||||
sortBy = "score";
|
||||
if (searchSortConfiguration != null && searchSortConfiguration.getDefaultSort() != null) {
|
||||
DiscoverySortFieldConfiguration defaultSort = searchSortConfiguration.getDefaultSort();
|
||||
if (Objects.nonNull(searchSortConfiguration.getSortFields()) &&
|
||||
!searchSortConfiguration.getSortFields().isEmpty()) {
|
||||
DiscoverySortFieldConfiguration defaultSort = searchSortConfiguration.getSortFields().get(0);
|
||||
if (StringUtils.isBlank(defaultSort.getMetadataField())) {
|
||||
return sortBy;
|
||||
}
|
||||
sortBy = defaultSort.getMetadataField();
|
||||
}
|
||||
return sortBy;
|
||||
@@ -378,7 +393,8 @@ public class DiscoverQueryBuilder implements InitializingBean {
|
||||
DiscoverFilterQuery filterQuery = searchService.toFilterQuery(context,
|
||||
filter.getIndexFieldName(),
|
||||
searchFilter.getOperator(),
|
||||
searchFilter.getValue());
|
||||
searchFilter.getValue(),
|
||||
discoveryConfiguration);
|
||||
|
||||
if (filterQuery != null) {
|
||||
filterQueries.add(filterQuery.getFilterQuery());
|
||||
|
@@ -108,7 +108,7 @@ public class Utils {
|
||||
/**
|
||||
* The default page size, if unspecified in the request.
|
||||
*/
|
||||
private static final int DEFAULT_PAGE_SIZE = 20;
|
||||
public static final int DEFAULT_PAGE_SIZE = 20;
|
||||
|
||||
/**
|
||||
* The maximum number of embed levels to allow.
|
||||
|
@@ -77,6 +77,18 @@ spring.http.encoding.force=true
|
||||
# However, you may wish to set this to "always" in your 'local.cfg' for development or debugging purposes.
|
||||
server.error.include-stacktrace = never
|
||||
|
||||
# Spring Boot proxy configuration (can be overridden in local.cfg).
|
||||
# By default, Spring Boot does not automatically use X-Forwarded-* Headers when generating links (and similar) in the
|
||||
# DSpace REST API. Three options are currently supported by Spring Boot:
|
||||
# * NATIVE = allows your web server to natively support standard Forwarded headers
|
||||
# * FRAMEWORK = (DSpace default) enables Spring Framework's built in filter to manage these headers in Spring Boot.
|
||||
# This setting is used by default to support all X-Forwarded-* headers, as the DSpace backend is often
|
||||
# installed behind Apache HTTPD or Nginx proxy (both of which pass those headers to Tomcat).
|
||||
# * NONE = (Spring default) Forwarded headers are ignored
|
||||
# For more information see
|
||||
# https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-use-behind-a-proxy-server
|
||||
server.forward-headers-strategy=FRAMEWORK
|
||||
|
||||
######################
|
||||
# Spring Boot Autoconfigure
|
||||
#
|
||||
@@ -107,6 +119,9 @@ spring.main.allow-bean-definition-overriding = true
|
||||
# Log4J configuration
|
||||
logging.config = ${dspace.dir}/config/log4j2.xml
|
||||
|
||||
##################################
|
||||
# Spring MVC file upload settings
|
||||
#
|
||||
# Maximum size of a single uploaded file (default = 1MB)
|
||||
spring.servlet.multipart.max-file-size = 512MB
|
||||
|
||||
|
@@ -98,39 +98,62 @@
|
||||
"onclick" : function() { toastr.remove(); }
|
||||
}
|
||||
|
||||
// When the login page loads, we do *two* AJAX requests.
|
||||
// (1) Call GET /api/authn/status. This call has two purposes. First, it checks to see if you are logged in,
|
||||
// (if not, WWW-Authenticate will return login options). Second, it retrieves the CSRF token, if a
|
||||
// new one has been assigned (as a valid CSRF token is required for the POST call).
|
||||
// (2) If that /api/authn/status call finds authentication data, call POST /api/authn/login.
|
||||
// This scenario occurs when you login via an external authentication system (e.g. Shibboleth)...
|
||||
// in which case the main role of /api/authn/login is to simply ensure the "Authorization" header
|
||||
// is sent back to the client (based on your authentication data).
|
||||
$.ajax({
|
||||
url : window.location.href.replace("login.html", "") + 'api/authn/login',
|
||||
type : 'POST',
|
||||
beforeSend: function (xhr, settings) {
|
||||
// If CSRF token found in cookie, send it back as X-XSRF-Token header
|
||||
var csrfToken = getCSRFToken();
|
||||
if (csrfToken != null) {
|
||||
xhr.setRequestHeader('X-XSRF-Token', csrfToken);
|
||||
}
|
||||
},
|
||||
success : successHandler,
|
||||
error : function(xhr, textStatus, errorThrown) {
|
||||
// Check for an update to the CSRF Token & save to a MyHalBrowserCsrfToken cookie (if found)
|
||||
checkForUpdatedCSRFTokenInResponse(xhr);
|
||||
url : window.location.href.replace("login.html", "") + 'api/authn/status',
|
||||
type : 'GET',
|
||||
success : function(result, status, xhr) {
|
||||
// Check for an update to the CSRF Token & save to a MyHalBrowserCsrfToken cookie (if found)
|
||||
checkForUpdatedCSRFTokenInResponse(xhr);
|
||||
|
||||
// If 401 Unauthorized, check WWW-Authenticate for authentication options
|
||||
if (xhr.status === 401) {
|
||||
var authenticate = xhr.getResponseHeader("WWW-Authenticate");
|
||||
var element = $('div.other-login-methods');
|
||||
if(authenticate !== null) {
|
||||
var realms = authenticate.match(/(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g);
|
||||
if (realms.length == 1){
|
||||
var loc = /location="([^,]*)"/.exec(authenticate);
|
||||
if (loc !== null && loc.length === 2) {
|
||||
document.location = loc[1];
|
||||
}
|
||||
} else if (realms.length > 1){
|
||||
for (var i = 0; i < realms.length; i++){
|
||||
addLocationButton(realms[i], element);
|
||||
}
|
||||
}
|
||||
// Check for WWW-Authenticate header. If found, this means we are not yet authenticated, and
|
||||
// therefore we need to display available authentication options.
|
||||
var authenticate = xhr.getResponseHeader("WWW-Authenticate");
|
||||
if (authenticate !== null) {
|
||||
var element = $('div.other-login-methods');
|
||||
var realms = authenticate.match(/(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g);
|
||||
if (realms.length == 1){
|
||||
var loc = /location="([^,]*)"/.exec(authenticate);
|
||||
if (loc !== null && loc.length === 2) {
|
||||
document.location = loc[1];
|
||||
}
|
||||
} else if (realms.length > 1){
|
||||
for (var i = 0; i < realms.length; i++){
|
||||
addLocationButton(realms[i], element);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If Authentication data was found, do a POST /api/authn/login to ensure that data's JWT
|
||||
// is sent back in the "Authorization" header. This simply completes an external authentication
|
||||
// process (e.g. Shibboleth)
|
||||
$.ajax({
|
||||
url : window.location.href.replace("login.html", "") + 'api/authn/login',
|
||||
type : 'POST',
|
||||
beforeSend: function (xhr, settings) {
|
||||
// If CSRF token found in cookie, send it back as X-XSRF-Token header
|
||||
var csrfToken = getCSRFToken();
|
||||
if (csrfToken != null) {
|
||||
xhr.setRequestHeader('X-XSRF-Token', csrfToken);
|
||||
}
|
||||
},
|
||||
success : successHandler,
|
||||
error : function(xhr, textStatus, errorThrown) {
|
||||
// Check for an update to the CSRF Token & save to a MyHalBrowserCsrfToken cookie (if found)
|
||||
checkForUpdatedCSRFTokenInResponse(xhr);
|
||||
toastr.error('Failed to logged in. Please check for errors in Javascript console.', 'Login Failed');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
error : function(xhr, textStatus, errorThrown) {
|
||||
toastr.error('Failed to connect with backend. Please check for errors in Javascript console.', 'Could Not Load');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -172,6 +195,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
// When the Username/Password Login form is submitted, POST that data directly to /api/authn/login.
|
||||
// This logs the user in and ensures the "Authorization" header is set with the JWT.
|
||||
$("#login-form").submit(function(event) {
|
||||
event.preventDefault();
|
||||
$.ajax({
|
||||
@@ -191,7 +216,9 @@
|
||||
}
|
||||
},
|
||||
success : successHandler,
|
||||
error : function() {
|
||||
error : function(xhr) {
|
||||
// Check for an update to the CSRF Token & save to a MyHalBrowserCsrfToken cookie (if found)
|
||||
checkForUpdatedCSRFTokenInResponse(xhr);
|
||||
toastr.error('The credentials you entered are invalid. Please try again.', 'Login Failed');
|
||||
}
|
||||
});
|
||||
|
@@ -17,6 +17,7 @@ import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
@@ -24,6 +25,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.io.InputStream;
|
||||
@@ -164,35 +166,111 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
|
||||
|
||||
@Test
|
||||
public void testStatusShibAuthenticatedWithCookie() throws Exception {
|
||||
//Enable Shibboleth login
|
||||
//Enable Shibboleth login only
|
||||
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
|
||||
|
||||
//Simulate that a shibboleth authentication has happened
|
||||
String token = getClient().perform(post("/api/authn/login")
|
||||
String uiURL = configurationService.getProperty("dspace.ui.url");
|
||||
|
||||
// In order to fully simulate a Shibboleth authentication, we'll call
|
||||
// /api/authn/shibboleth?redirectUrl=[UI-URL] , with valid Shibboleth request attributes.
|
||||
// In this situation, we are mocking how Shibboleth works from our UI (see also ShibbolethRestController):
|
||||
// (1) The UI sends the user to Shibboleth to login
|
||||
// (2) After a successful login, Shibboleth redirects user to /api/authn/shibboleth?redirectUrl=[url]
|
||||
// (3) That triggers generation of the auth token (JWT), and redirects the user to 'redirectUrl', sending along
|
||||
// a temporary cookie containing the auth token.
|
||||
// In below call, we're sending a GET request (as that's what a redirect is), with a Referer of a "fake"
|
||||
// Shibboleth server to simulate this request coming back from Shibboleth (after a successful login).
|
||||
// We are then verifying the user will be redirected to the 'redirectUrl' with a single-use auth cookie
|
||||
// (NOTE: Additional tests of this /api/authn/shibboleth endpoint can be found in ShibbolethRestControllerIT)
|
||||
Cookie authCookie = getClient().perform(get("/api/authn/shibboleth")
|
||||
.header("Referer", "https://myshib.example.com")
|
||||
.param("redirectUrl", uiURL)
|
||||
.requestAttr("SHIB-MAIL", eperson.getEmail())
|
||||
.requestAttr("SHIB-SCOPED-AFFILIATION", "faculty;staff"))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn().getResponse().getHeader(AUTHORIZATION_HEADER).replace("Bearer ", "");
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl(uiURL))
|
||||
// Verify that the CSRF token has NOT been changed. Creating the auth cookie should NOT change our CSRF
|
||||
// token. The CSRF token should only change when we call /login with the cookie (see later in this test)
|
||||
.andExpect(cookie().doesNotExist("DSPACE-XSRF-COOKIE"))
|
||||
.andExpect(header().doesNotExist("DSPACE-XSRF-TOKEN"))
|
||||
.andExpect(cookie().exists(AUTHORIZATION_COOKIE))
|
||||
.andReturn().getResponse().getCookie(AUTHORIZATION_COOKIE);
|
||||
|
||||
Cookie[] cookies = new Cookie[1];
|
||||
cookies[0] = new Cookie(AUTHORIZATION_COOKIE, token);
|
||||
// Verify the temporary cookie now exists & obtain its token for use below
|
||||
assertNotNull(authCookie);
|
||||
String token = authCookie.getValue();
|
||||
|
||||
//Check if we are authenticated with a status request with authorization cookie
|
||||
getClient().perform(get("/api/authn/status")
|
||||
.secure(true)
|
||||
.cookie(cookies))
|
||||
// This step is _not required_ to successfully authenticate, but it mocks the behavior of our UI & HAL Browser.
|
||||
// We'll send a "/status" request to the REST API with our auth cookie. This should return that we have a
|
||||
// *valid* authentication (as auth cookie is valid), however the cookie will remain. To complete the login
|
||||
// process we MUST call the "/login" endpoint (see the next step in this test).
|
||||
// (NOTE that this call has an "Origin" matching the UI, to better mock that the request came from there &
|
||||
// to verify the temporary auth cookie is valid for the UI's origin.)
|
||||
getClient().perform(get("/api/authn/status").header("Origin", uiURL)
|
||||
.secure(true)
|
||||
.cookie(authCookie))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$.okay", is(true)))
|
||||
.andExpect(jsonPath("$.authenticated", is(true)))
|
||||
.andExpect(jsonPath("$.type", is("status")))
|
||||
// Verify that the CSRF token has NOT been changed... status checks won't change the token
|
||||
// (only login/logout will)
|
||||
.andExpect(cookie().doesNotExist("DSPACE-XSRF-COOKIE"))
|
||||
.andExpect(header().doesNotExist("DSPACE-XSRF-TOKEN"));
|
||||
|
||||
// To complete the authentication process, we pass our auth cookie to the "/login" endpoint.
|
||||
// This is where the temporary cookie will be read, verified & destroyed. After this point, the UI will
|
||||
// only use the 'Authorization' header for all future requests.
|
||||
// (NOTE that this call has an "Origin" matching the UI, to better mock that the request came from there &
|
||||
// to verify the temporary auth cookie is valid for the UI's origin.)
|
||||
getClient().perform(post("/api/authn/login").header("Origin", uiURL)
|
||||
.secure(true)
|
||||
.cookie(authCookie))
|
||||
.andExpect(status().isOk())
|
||||
// Verify the Auth cookie has been destroyed
|
||||
.andExpect(cookie().value(AUTHORIZATION_COOKIE, ""))
|
||||
// Verify token is now sent back in the Authorization header as the Bearer token
|
||||
.andExpect(header().string(AUTHORIZATION_HEADER, "Bearer " + token))
|
||||
// Verify that the CSRF token has been changed
|
||||
// (as both cookie and header should be sent back)
|
||||
.andExpect(cookie().exists("DSPACE-XSRF-COOKIE"))
|
||||
.andExpect(header().exists("DSPACE-XSRF-TOKEN"));
|
||||
|
||||
// Now that the auth cookie is cleared, all future requests (from UI)
|
||||
// should be made via the Authorization header. So, this tests the token is still valid if sent via header.
|
||||
getClient(token).perform(get("/api/authn/status").header("Origin", uiURL))
|
||||
.andExpect(status().isOk())
|
||||
//We expect the content type to be "application/hal+json;charset=UTF-8"
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$.okay", is(true)))
|
||||
.andExpect(jsonPath("$.authenticated", is(true)))
|
||||
.andExpect(jsonPath("$.type", is("status")));
|
||||
|
||||
//Logout
|
||||
getClient(token).perform(post("/api/authn/logout"))
|
||||
//Logout, invalidating the token
|
||||
getClient(token).perform(post("/api/authn/logout").header("Origin", uiURL))
|
||||
.andExpect(status().isNoContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShibbolethEndpointCannotBeUsedWithShibDisabled() throws Exception {
|
||||
// Enable only password login
|
||||
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", PASS_ONLY);
|
||||
|
||||
String uiURL = configurationService.getProperty("dspace.ui.url");
|
||||
|
||||
// Verify /api/authn/shibboleth endpoint does not work
|
||||
// NOTE: this is the same call as in testStatusShibAuthenticatedWithCookie())
|
||||
getClient().perform(get("/api/authn/shibboleth")
|
||||
.header("Referer", "https://myshib.example.com")
|
||||
.param("redirectUrl", uiURL)
|
||||
.requestAttr("SHIB-MAIL", eperson.getEmail())
|
||||
.requestAttr("SHIB-SCOPED-AFFILIATION", "faculty;staff"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
// NOTE: This test is similar to testStatusShibAuthenticatedWithCookie(), but proves the same process works
|
||||
// for Password Authentication in theory (NOTE: at this time, there's no way to create an auth cookie via the
|
||||
// Password Authentication process).
|
||||
@Test
|
||||
public void testStatusPasswordAuthenticatedWithCookie() throws Exception {
|
||||
// Login via password to retrieve a valid token
|
||||
@@ -201,21 +279,49 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
|
||||
// Remove "Bearer " from that token, so that we are left with the token itself
|
||||
token = token.replace("Bearer ", "");
|
||||
|
||||
// Save token to an Authorization cookie
|
||||
Cookie[] cookies = new Cookie[1];
|
||||
cookies[0] = new Cookie(AUTHORIZATION_COOKIE, token);
|
||||
// Fake the creation of an auth cookie, just for testing. (Currently, it's not possible to create an auth cookie
|
||||
// via Password auth, but this test proves it would work if enabled)
|
||||
Cookie authCookie = new Cookie(AUTHORIZATION_COOKIE, token);
|
||||
|
||||
//Check if we are authenticated with a status request using authorization cookie
|
||||
getClient().perform(get("/api/authn/status")
|
||||
.secure(true)
|
||||
.cookie(cookies))
|
||||
// Now, similar to how both the UI & Hal Browser authentication works, send a "/status" request to the REST API
|
||||
// with our auth cookie. This should return that we *have a valid* authentication (in the auth cookie).
|
||||
// However, this is just a validation check, so this auth cookie will remain. To complete the login process
|
||||
// we'll need to call the "/login" endpoint (see the next step in this test).
|
||||
getClient().perform(get("/api/authn/status").secure(true).cookie(authCookie))
|
||||
.andExpect(status().isOk())
|
||||
//We expect the content type to be "application/hal+json"
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$.okay", is(true)))
|
||||
.andExpect(jsonPath("$.authenticated", is(true)))
|
||||
.andExpect(jsonPath("$.type", is("status")));
|
||||
//Logout
|
||||
.andExpect(jsonPath("$.type", is("status")))
|
||||
// Verify that the CSRF token has NOT been changed... status checks won't change the token
|
||||
// (only login/logout will)
|
||||
.andExpect(cookie().doesNotExist("DSPACE-XSRF-COOKIE"))
|
||||
.andExpect(header().doesNotExist("DSPACE-XSRF-TOKEN"));
|
||||
|
||||
// To complete the authentication process, we pass our auth cookie to the "/login" endpoint.
|
||||
// This is where the temporary cookie will be read, verified & destroyed. After this point, the UI will
|
||||
// only use the Authorization header for all future requests.
|
||||
getClient().perform(post("/api/authn/login").secure(true).cookie(authCookie))
|
||||
.andExpect(status().isOk())
|
||||
// Verify the Auth cookie has been destroyed
|
||||
.andExpect(cookie().value(AUTHORIZATION_COOKIE, ""))
|
||||
// Verify token is now sent back in the Authorization header
|
||||
.andExpect(header().string(AUTHORIZATION_HEADER, "Bearer " + token))
|
||||
// Verify that the CSRF token has been changed
|
||||
// (as both cookie and header should be sent back)
|
||||
.andExpect(cookie().exists("DSPACE-XSRF-COOKIE"))
|
||||
.andExpect(header().exists("DSPACE-XSRF-TOKEN"));
|
||||
|
||||
// Now that the auth cookie is cleared, all future requests (from UI)
|
||||
// should be made via the Authorization header. So, this tests the token is still valid if sent via header.
|
||||
getClient(token).perform(get("/api/authn/status"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$.okay", is(true)))
|
||||
.andExpect(jsonPath("$.authenticated", is(true)))
|
||||
.andExpect(jsonPath("$.type", is("status")));
|
||||
|
||||
// Logout, invalidating the token
|
||||
getClient(token).perform(post("/api/authn/logout"))
|
||||
.andExpect(status().isNoContent());
|
||||
}
|
||||
@@ -1087,7 +1193,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
|
||||
String loginToken = getAuthToken(eperson.getEmail(), password);
|
||||
getClient().perform(get("/api/core/bitstreams/" + bitstream.getID()
|
||||
+ "/content?authentication-token=" + loginToken))
|
||||
.andExpect(status().isForbidden());
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1098,7 +1204,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
|
||||
Thread.sleep(1);
|
||||
getClient().perform(get("/api/core/bitstreams/" + bitstream.getID()
|
||||
+ "/content?authentication-token=" + shortLivedToken))
|
||||
.andExpect(status().isForbidden());
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -2478,4 +2478,118 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
|
||||
getClient().perform(get("/api/core/communities/search/findAdminAuthorized"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAllSearchTopEmbeddedPaginationTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||
.withName("Parent Community")
|
||||
.withLogo("ThisIsSomeDummyText")
|
||||
.build();
|
||||
|
||||
Collection col = CollectionBuilder.createCollection(context, parentCommunity)
|
||||
.withName("Collection 1").build();
|
||||
|
||||
Collection col2 = CollectionBuilder.createCollection(context, parentCommunity)
|
||||
.withName("Collection 2").build();
|
||||
|
||||
CommunityBuilder.createCommunity(context)
|
||||
.withName("Parent Community 2")
|
||||
.withLogo("SomeTest").build();
|
||||
|
||||
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||
.withName("Sub Community").build();
|
||||
|
||||
Community child2 = CommunityBuilder.createSubCommunity(context, parentCommunity)
|
||||
.withName("Sub Community 2").build();
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
getClient().perform(get("/api/core/communities/search/top")
|
||||
.param("size", "1")
|
||||
.param("embed", "subcommunities")
|
||||
.param("embed", "collections")
|
||||
.param("embed.size", "subcommunities=1")
|
||||
.param("embed.size", "collections=1"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$._embedded.communities", Matchers.contains(
|
||||
CommunityMatcher.matchCommunity(parentCommunity))))
|
||||
// Verify subcommunities
|
||||
.andExpect(jsonPath("$._embedded.communities[0]._embedded.subcommunities._embedded.subcommunities",
|
||||
Matchers.contains(CommunityMatcher.matchCommunity(child1))))
|
||||
.andExpect(jsonPath("$._embedded.communities[0]._embedded.subcommunities._links.self.href",
|
||||
Matchers.containsString("/api/core/communities/" + parentCommunity.getID()
|
||||
+ "/subcommunities?size=1")))
|
||||
.andExpect(jsonPath("$._embedded.communities[0]._embedded.subcommunities._links.next.href",
|
||||
Matchers.containsString("/api/core/communities/" + parentCommunity.getID()
|
||||
+ "/subcommunities?page=1&size=1")))
|
||||
.andExpect(jsonPath("$._embedded.communities[0]._embedded.subcommunities._links.last.href",
|
||||
Matchers.containsString("/api/core/communities/" + parentCommunity.getID()
|
||||
+ "/subcommunities?page=1&size=1")))
|
||||
// Verify collections
|
||||
.andExpect(jsonPath("$._embedded.communities[0]._embedded.collections._embedded.collections",
|
||||
Matchers.contains(CollectionMatcher.matchCollection(col))))
|
||||
.andExpect(jsonPath("$._embedded.communities[0]._embedded.collections._links.self.href",
|
||||
Matchers.containsString("/api/core/communities/" + parentCommunity.getID()
|
||||
+ "/collections?size=1")))
|
||||
.andExpect(jsonPath("$._embedded.communities[0]._embedded.collections._links.next.href",
|
||||
Matchers.containsString("/api/core/communities/" + parentCommunity.getID()
|
||||
+ "/collections?page=1&size=1")))
|
||||
.andExpect(jsonPath("$._embedded.communities[0]._embedded.collections._links.last.href",
|
||||
Matchers.containsString("/api/core/communities/" + parentCommunity.getID()
|
||||
+ "/collections?page=1&size=1")))
|
||||
|
||||
.andExpect(jsonPath("$._links.self.href",
|
||||
Matchers.containsString("/api/core/communities/search/top?size=1")))
|
||||
.andExpect(jsonPath("$._links.first.href",
|
||||
Matchers.containsString("/api/core/communities/search/top?page=0&size=1")))
|
||||
.andExpect(jsonPath("$._links.next.href",
|
||||
Matchers.containsString("/api/core/communities/search/top?page=1&size=1")))
|
||||
.andExpect(jsonPath("$._links.last.href",
|
||||
Matchers.containsString("/api/core/communities/search/top?page=1&size=1")))
|
||||
.andExpect(jsonPath("$.page.size", is(1)))
|
||||
.andExpect(jsonPath("$.page.totalPages", is(2)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(2)));
|
||||
|
||||
getClient().perform(get("/api/core/communities/search/top")
|
||||
.param("size", "1")
|
||||
.param("embed", "subcommunities")
|
||||
.param("embed", "collections")
|
||||
.param("embed.size", "subcommunities=2")
|
||||
.param("embed.size", "collections=2"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$._embedded.communities", Matchers.contains(
|
||||
CommunityMatcher.matchCommunity(parentCommunity))))
|
||||
// Verify subcommunities
|
||||
.andExpect(jsonPath("$._embedded.communities[0]._embedded.subcommunities._embedded.subcommunities",
|
||||
Matchers.containsInAnyOrder(CommunityMatcher.matchCommunity(child1),
|
||||
CommunityMatcher.matchCommunity(child2)
|
||||
)))
|
||||
.andExpect(jsonPath("$._embedded.communities[0]._embedded.subcommunities._links.self.href",
|
||||
Matchers.containsString("/api/core/communities/" + parentCommunity.getID()
|
||||
+ "/subcommunities?size=2")))
|
||||
// Verify collections
|
||||
.andExpect(jsonPath("$._embedded.communities[0]._embedded.collections._embedded.collections",
|
||||
Matchers.containsInAnyOrder(CollectionMatcher.matchCollection(col),
|
||||
CollectionMatcher.matchCollection(col2)
|
||||
)))
|
||||
.andExpect(jsonPath("$._embedded.communities[0]._embedded.collections._links.self.href",
|
||||
Matchers.containsString("/api/core/communities/" + parentCommunity.getID()
|
||||
+ "/collections?size=2")))
|
||||
|
||||
.andExpect(jsonPath("$._links.self.href",
|
||||
Matchers.containsString("/api/core/communities/search/top?size=1")))
|
||||
.andExpect(jsonPath("$._links.first.href",
|
||||
Matchers.containsString("/api/core/communities/search/top?page=0&size=1")))
|
||||
.andExpect(jsonPath("$._links.next.href",
|
||||
Matchers.containsString("/api/core/communities/search/top?page=1&size=1")))
|
||||
.andExpect(jsonPath("$._links.last.href",
|
||||
Matchers.containsString("/api/core/communities/search/top?page=1&size=1")))
|
||||
.andExpect(jsonPath("$.page.size", is(1)))
|
||||
.andExpect(jsonPath("$.page.totalPages", is(2)))
|
||||
.andExpect(jsonPath("$.page.totalElements", is(2)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -55,6 +55,7 @@ import org.dspace.content.Item;
|
||||
import org.dspace.content.WorkspaceItem;
|
||||
import org.dspace.content.authority.Choices;
|
||||
import org.dspace.content.authority.service.MetadataAuthorityService;
|
||||
import org.dspace.discovery.configuration.DiscoverySortFieldConfiguration;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.eperson.Group;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
@@ -988,14 +989,74 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
|
||||
SearchFilterMatcher.isJournalOfPublicationRelation()
|
||||
)))
|
||||
//These sortOptions need to be present as it's the default in the configuration
|
||||
.andExpect(jsonPath("$.sortOptions", containsInAnyOrder(
|
||||
SortOptionMatcher.titleSortOption(),
|
||||
SortOptionMatcher.dateIssuedSortOption(),
|
||||
SortOptionMatcher.dateAccessionedSortOption(),
|
||||
SortOptionMatcher.scoreSortOption()
|
||||
.andExpect(jsonPath("$.sortOptions", contains(
|
||||
SortOptionMatcher.sortOptionMatcher(
|
||||
"score", DiscoverySortFieldConfiguration.SORT_ORDER.desc.name()),
|
||||
SortOptionMatcher.sortOptionMatcher(
|
||||
"dc.title", DiscoverySortFieldConfiguration.SORT_ORDER.asc.name()),
|
||||
SortOptionMatcher.sortOptionMatcher(
|
||||
"dc.date.issued", DiscoverySortFieldConfiguration.SORT_ORDER.desc.name()),
|
||||
SortOptionMatcher.sortOptionMatcher(
|
||||
"dc.date.accessioned", DiscoverySortFieldConfiguration.SORT_ORDER.desc.name())
|
||||
)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkSortOrderInPersonOrOrgunitConfigurationTest() throws Exception {
|
||||
getClient().perform(get("/api/discover/search")
|
||||
.param("configuration", "personOrOrgunit"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.type", is("discover")))
|
||||
.andExpect(jsonPath("$._links.objects.href", containsString("api/discover/search/objects")))
|
||||
.andExpect(jsonPath("$._links.self.href", containsString("api/discover/search")))
|
||||
.andExpect(jsonPath("$.sortOptions", contains(
|
||||
SortOptionMatcher.sortOptionMatcher("dspace.entity.type",
|
||||
DiscoverySortFieldConfiguration.SORT_ORDER.desc.name()),
|
||||
SortOptionMatcher.sortOptionMatcher("organization.legalName",
|
||||
DiscoverySortFieldConfiguration.SORT_ORDER.asc.name()),
|
||||
SortOptionMatcher.sortOptionMatcher("organisation.address.addressCountry",
|
||||
DiscoverySortFieldConfiguration.SORT_ORDER.asc.name()),
|
||||
SortOptionMatcher.sortOptionMatcher("organisation.address.addressLocality",
|
||||
DiscoverySortFieldConfiguration.SORT_ORDER.asc.name()),
|
||||
SortOptionMatcher.sortOptionMatcher("organisation.foundingDate",
|
||||
DiscoverySortFieldConfiguration.SORT_ORDER.desc.name()),
|
||||
SortOptionMatcher.sortOptionMatcher("dc.date.accessioned",
|
||||
DiscoverySortFieldConfiguration.SORT_ORDER.desc.name()),
|
||||
SortOptionMatcher.sortOptionMatcher("person.familyName",
|
||||
DiscoverySortFieldConfiguration.SORT_ORDER.asc.name()),
|
||||
SortOptionMatcher.sortOptionMatcher("person.givenName",
|
||||
DiscoverySortFieldConfiguration.SORT_ORDER.asc.name()),
|
||||
SortOptionMatcher.sortOptionMatcher("person.birthDate",
|
||||
DiscoverySortFieldConfiguration.SORT_ORDER.desc.name())
|
||||
)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void discoverSearchByFieldNotConfiguredTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
|
||||
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
|
||||
.withName("Collection 1")
|
||||
.build();
|
||||
|
||||
ItemBuilder.createItem(context, col1)
|
||||
.withTitle("Test")
|
||||
.withIssueDate("2010-10-17")
|
||||
.withAuthor("Testing, Works")
|
||||
.withSubject("ExtraEntry").build();
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
getClient().perform(get("/api/discover/search/objects")
|
||||
.param("sort", "dc.date.accessioned, ASC")
|
||||
.param("configuration", "workspace"))
|
||||
.andExpect(status().isUnprocessableEntity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void discoverSearchObjectsTest() throws Exception {
|
||||
|
||||
@@ -5177,6 +5238,137 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
|
||||
.andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void discoverSearchObjectsTestForAdministrativeViewWithFiltersEquals() throws Exception {
|
||||
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
parentCommunity = CommunityBuilder
|
||||
.createCommunity(context)
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
Community child1 = CommunityBuilder
|
||||
.createSubCommunity(context, parentCommunity)
|
||||
.withName("Sub Community")
|
||||
.build();
|
||||
Collection col1 = CollectionBuilder
|
||||
.createCollection(context, child1)
|
||||
.withName("Collection 1")
|
||||
.build();
|
||||
Collection col2 = CollectionBuilder
|
||||
.createCollection(context, child1)
|
||||
.withName("Collection 2")
|
||||
.build();
|
||||
|
||||
ItemBuilder.createItem(context, col1)
|
||||
.withTitle("Public Test Item")
|
||||
.withIssueDate("2010-10-17")
|
||||
.withAuthor("Smith, Donald")
|
||||
.withSubject("ExtraEntry")
|
||||
.build();
|
||||
|
||||
ItemBuilder.createItem(context, col2)
|
||||
.withTitle("Withdrawn Test Item")
|
||||
.withIssueDate("1990-02-13")
|
||||
.withAuthor("Smith, Maria")
|
||||
.withAuthor("Doe, Jane")
|
||||
.withSubject("ExtraEntry")
|
||||
.withdrawn()
|
||||
.build();
|
||||
|
||||
ItemBuilder.createItem(context, col2)
|
||||
.withTitle("Private Test Item")
|
||||
.withIssueDate("2010-02-13")
|
||||
.withAuthor("Smith, Maria")
|
||||
.withAuthor("Doe, Jane")
|
||||
.withSubject("AnotherTest")
|
||||
.withSubject("ExtraEntry")
|
||||
.makeUnDiscoverable()
|
||||
.build();
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
|
||||
getClient(adminToken)
|
||||
.perform(get("/api/discover/search/objects")
|
||||
.param("configuration", "administrativeView")
|
||||
.param("query", "Test")
|
||||
.param("f.withdrawn", "true,equals")
|
||||
)
|
||||
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.type", is("discover")))
|
||||
.andExpect(jsonPath("$._embedded.searchResult.page", is(
|
||||
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 1)
|
||||
)))
|
||||
.andExpect(jsonPath("$._embedded.searchResult._embedded.objects",
|
||||
Matchers.contains(
|
||||
SearchResultMatcher.matchOnItemName("item", "items", "Withdrawn Test Item")
|
||||
)
|
||||
))
|
||||
.andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")));
|
||||
|
||||
getClient(adminToken)
|
||||
.perform(get("/api/discover/search/objects")
|
||||
.param("configuration", "administrativeView")
|
||||
.param("query", "Test")
|
||||
.param("f.withdrawn", "false,equals")
|
||||
)
|
||||
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.type", is("discover")))
|
||||
.andExpect(jsonPath("$._embedded.searchResult.page", is(
|
||||
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 2)
|
||||
)))
|
||||
.andExpect(jsonPath("$._embedded.searchResult._embedded.objects",
|
||||
Matchers.containsInAnyOrder(
|
||||
SearchResultMatcher.matchOnItemName("item", "items", "Public Test Item"),
|
||||
SearchResultMatcher.matchOnItemName("item", "items", "Private Test Item")
|
||||
)
|
||||
))
|
||||
.andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")));
|
||||
|
||||
getClient(adminToken)
|
||||
.perform(get("/api/discover/search/objects")
|
||||
.param("configuration", "administrativeView")
|
||||
.param("query", "Test")
|
||||
.param("f.discoverable", "true,equals")
|
||||
)
|
||||
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.type", is("discover")))
|
||||
.andExpect(jsonPath("$._embedded.searchResult.page", is(
|
||||
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 2)
|
||||
)))
|
||||
.andExpect(jsonPath("$._embedded.searchResult._embedded.objects",
|
||||
Matchers.containsInAnyOrder(
|
||||
SearchResultMatcher.matchOnItemName("item", "items", "Public Test Item"),
|
||||
SearchResultMatcher.matchOnItemName("item", "items", "Withdrawn Test Item")
|
||||
)
|
||||
))
|
||||
.andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")));
|
||||
|
||||
getClient(adminToken)
|
||||
.perform(get("/api/discover/search/objects")
|
||||
.param("configuration", "administrativeView")
|
||||
.param("query", "Test")
|
||||
.param("f.discoverable", "false,equals")
|
||||
)
|
||||
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.type", is("discover")))
|
||||
.andExpect(jsonPath("$._embedded.searchResult.page", is(
|
||||
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 1)
|
||||
)))
|
||||
.andExpect(jsonPath("$._embedded.searchResult._embedded.objects",
|
||||
Matchers.contains(
|
||||
SearchResultMatcher.matchOnItemName("item", "items", "Private Test Item")
|
||||
)
|
||||
))
|
||||
.andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void discoverSearchPoolTaskObjectsTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
@@ -25,6 +25,7 @@ import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.dspace.app.rest.matcher.MetadataMatcher;
|
||||
import org.dspace.app.rest.model.MetadataValueRest;
|
||||
@@ -49,6 +50,7 @@ import org.dspace.content.service.EntityTypeService;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.content.service.RelationshipTypeService;
|
||||
import org.dspace.content.service.WorkspaceItemService;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -81,6 +83,8 @@ public class PatchMetadataIT extends AbstractEntityIntegrationTest {
|
||||
|
||||
private List<String> authorsOriginalOrder;
|
||||
|
||||
private List<MetadataValue> authorsMetadataOriginalOrder;
|
||||
|
||||
private AtomicReference<Integer> idRef1;
|
||||
private AtomicReference<Integer> idRef2;
|
||||
|
||||
@@ -202,19 +206,19 @@ public class PatchMetadataIT extends AbstractEntityIntegrationTest {
|
||||
.andDo(result -> idRef2.set(read(result.getResponse().getContentAsString(), "$.id")));
|
||||
|
||||
publication = workspaceItemService.find(context, publicationItem.getID());
|
||||
List<MetadataValue> publicationAuthorList =
|
||||
authorsMetadataOriginalOrder =
|
||||
itemService.getMetadata(publication.getItem(), "dc", "contributor", "author", Item.ANY);
|
||||
assertEquals(publicationAuthorList.size(), 5);
|
||||
assertThat(publicationAuthorList.get(0).getValue(), equalTo(authorsOriginalOrder.get(0)));
|
||||
assertThat(publicationAuthorList.get(0).getAuthority(), not(startsWith("virtual::")));
|
||||
assertThat(publicationAuthorList.get(1).getValue(), equalTo(authorsOriginalOrder.get(1)));
|
||||
assertThat(publicationAuthorList.get(1).getAuthority(), startsWith("virtual::"));
|
||||
assertThat(publicationAuthorList.get(2).getValue(), equalTo(authorsOriginalOrder.get(2)));
|
||||
assertThat(publicationAuthorList.get(2).getAuthority(), not(startsWith("virtual::")));
|
||||
assertThat(publicationAuthorList.get(3).getValue(), equalTo(authorsOriginalOrder.get(3)));
|
||||
assertThat(publicationAuthorList.get(3).getAuthority(), not(startsWith("virtual::")));
|
||||
assertThat(publicationAuthorList.get(4).getValue(), equalTo(authorsOriginalOrder.get(4)));
|
||||
assertThat(publicationAuthorList.get(4).getAuthority(), startsWith("virtual::"));
|
||||
assertEquals(authorsMetadataOriginalOrder.size(), 5);
|
||||
assertThat(authorsMetadataOriginalOrder.get(0).getValue(), equalTo(authorsOriginalOrder.get(0)));
|
||||
assertThat(authorsMetadataOriginalOrder.get(0).getAuthority(), not(startsWith("virtual::")));
|
||||
assertThat(authorsMetadataOriginalOrder.get(1).getValue(), equalTo(authorsOriginalOrder.get(1)));
|
||||
assertThat(authorsMetadataOriginalOrder.get(1).getAuthority(), startsWith("virtual::"));
|
||||
assertThat(authorsMetadataOriginalOrder.get(2).getValue(), equalTo(authorsOriginalOrder.get(2)));
|
||||
assertThat(authorsMetadataOriginalOrder.get(2).getAuthority(), not(startsWith("virtual::")));
|
||||
assertThat(authorsMetadataOriginalOrder.get(3).getValue(), equalTo(authorsOriginalOrder.get(3)));
|
||||
assertThat(authorsMetadataOriginalOrder.get(3).getAuthority(), not(startsWith("virtual::")));
|
||||
assertThat(authorsMetadataOriginalOrder.get(4).getValue(), equalTo(authorsOriginalOrder.get(4)));
|
||||
assertThat(authorsMetadataOriginalOrder.get(4).getAuthority(), startsWith("virtual::"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1162,6 +1166,59 @@ public class PatchMetadataIT extends AbstractEntityIntegrationTest {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This test will overwrite all authors (dc.contributor.author) of a workspace publication's "traditionalpageone"
|
||||
* section using a PATCH add with the entire array values.
|
||||
* It makes sure that virtual values are correctly reordered or deleted.
|
||||
*/
|
||||
@Test
|
||||
public void patchAddAllAuthorsOnTraditionalPageTest() throws Exception {
|
||||
|
||||
// "Whyte, William"
|
||||
// "Dahlen, Sarah" (virtual)
|
||||
// "Peterson, Karrie"
|
||||
// "Perotti, Enrico"
|
||||
// "Linton, Oliver" (virtual)
|
||||
initPersonPublicationWorkspace();
|
||||
|
||||
List<MetadataValue> expectedValues = new ArrayList<MetadataValue>();
|
||||
expectedValues.add(this.authorsMetadataOriginalOrder.get(2)); // "Peterson, Karrie"
|
||||
expectedValues.add(this.authorsMetadataOriginalOrder.get(4)); // "Linton, Oliver" (virtual)
|
||||
expectedValues.add(this.authorsMetadataOriginalOrder.get(0)); // "Whyte, William"
|
||||
patchAddEntireArray(expectedValues);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This test will overwrite all authors (dc.contributor.author) of a workspace publication's "traditionalpageone"
|
||||
* section using a PATCH add with an array composed by only a not existent virtual metadata.
|
||||
*/
|
||||
@Test
|
||||
public void patchAddAllAuthorsOnTraditionalPageNotExistentRelationTest() throws Exception {
|
||||
|
||||
initPersonPublicationWorkspace();
|
||||
|
||||
List<Operation> ops = new ArrayList<Operation>();
|
||||
List<MetadataValueRest> value = new ArrayList<MetadataValueRest>();
|
||||
|
||||
MetadataValueRest mrv = new MetadataValueRest();
|
||||
value.add(mrv);
|
||||
mrv.setValue("Dumbar, John");
|
||||
mrv.setAuthority("virtual::" + Integer.MAX_VALUE);
|
||||
|
||||
AddOperation add = new AddOperation("/sections/traditionalpageone/dc.contributor.author", value);
|
||||
ops.add(add);
|
||||
String patchBody = getPatchContent(ops);
|
||||
|
||||
String token = getAuthToken(admin.getEmail(), password);
|
||||
|
||||
getClient(token).perform(patch("/api/submission/workspaceitems/" + publicationItem.getID())
|
||||
.content(patchBody)
|
||||
.contentType(javax.ws.rs.core.MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||
.andExpect(status().isUnprocessableEntity());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1305,6 +1362,50 @@ public class PatchMetadataIT extends AbstractEntityIntegrationTest {
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method set the entire authors list (dc.contributor.author) within a workspace
|
||||
* publication's "traditionalpageone" section
|
||||
* @param metadataValues The metadata list of all the metadata values
|
||||
*/
|
||||
private void patchAddEntireArray(List<MetadataValue> metadataValues) throws Exception {
|
||||
List<Operation> ops = new ArrayList<Operation>();
|
||||
List<MetadataValueRest> value = new ArrayList<MetadataValueRest>();
|
||||
|
||||
// generates the MetadataValueRest list
|
||||
metadataValues.stream().forEach(mv -> {
|
||||
MetadataValueRest mrv = new MetadataValueRest();
|
||||
value.add(mrv);
|
||||
mrv.setValue(mv.getValue());
|
||||
if (mv.getAuthority() != null && mv.getAuthority().startsWith("virtual::")) {
|
||||
mrv.setAuthority(mv.getAuthority());
|
||||
mrv.setConfidence(mv.getConfidence());
|
||||
}
|
||||
});
|
||||
|
||||
AddOperation add = new AddOperation("/sections/traditionalpageone/dc.contributor.author", value);
|
||||
ops.add(add);
|
||||
String patchBody = getPatchContent(ops);
|
||||
|
||||
String token = getAuthToken(admin.getEmail(), password);
|
||||
|
||||
getClient(token).perform(patch("/api/submission/workspaceitems/" + publicationItem.getID())
|
||||
.content(patchBody)
|
||||
.contentType(javax.ws.rs.core.MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
final String authorField = "dc.contributor.author";
|
||||
final List<Matcher<? super Object>> matchers = new ArrayList<>();
|
||||
IntStream.range(0, metadataValues.size()).forEach((i) -> {
|
||||
matchers.add(Matchers.is(MetadataMatcher.matchMetadata(authorField, metadataValues.get(i).getValue(), i)));
|
||||
});
|
||||
|
||||
|
||||
getClient(token).perform(get("/api/submission/workspaceitems/" + publicationItem.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$.sections.traditionalpageone", Matchers.allOf(matchers)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a move operation on a workspace item's "traditionalpageone" section for
|
||||
* metadata field "dc.contributor.author".
|
||||
|
@@ -27,58 +27,96 @@ public class ShibbolethRestControllerIT extends AbstractControllerIntegrationTes
|
||||
@Autowired
|
||||
ConfigurationService configurationService;
|
||||
|
||||
public static final String[] PASS_ONLY = {"org.dspace.authenticate.PasswordAuthentication"};
|
||||
public static final String[] SHIB_ONLY = {"org.dspace.authenticate.ShibAuthentication"};
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
super.setUp();
|
||||
// Add a second trusted host for some tests
|
||||
configurationService.setProperty("rest.cors.allowed-origins",
|
||||
"${dspace.ui.url}, http://anotherdspacehost:4000");
|
||||
|
||||
// Enable Shibboleth login for all tests
|
||||
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedirectToDefaultDspaceUrl() throws Exception {
|
||||
String token = getAuthToken(eperson.getEmail(), password);
|
||||
|
||||
getClient(token).perform(get("/api/authn/shibboleth"))
|
||||
// NOTE: The initial call to /shibboleth comes *from* an external Shibboleth site. So, it is always
|
||||
// unauthenticated, but it must include some expected SHIB attributes.
|
||||
// SHIB-MAIL attribute is the default email header sent from Shibboleth after a successful login.
|
||||
// In this test we are simply mocking that behavior by setting it to an existing EPerson.
|
||||
getClient().perform(get("/api/authn/shibboleth").requestAttr("SHIB-MAIL", eperson.getEmail()))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("http://localhost:4000"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedirectToGivenTrustedUrl() throws Exception {
|
||||
|
||||
String token = getAuthToken(eperson.getEmail(), password);
|
||||
|
||||
getClient(token).perform(get("/api/authn/shibboleth")
|
||||
.param("redirectUrl", "http://localhost:8080/server/api/authn/status"))
|
||||
getClient().perform(get("/api/authn/shibboleth")
|
||||
.param("redirectUrl", "http://localhost:8080/server/api/authn/status")
|
||||
.requestAttr("SHIB-MAIL", eperson.getEmail()))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("http://localhost:8080/server/api/authn/status"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoRedirectIfShibbolethDisabled() throws Exception {
|
||||
// Enable Password authentication ONLY
|
||||
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", PASS_ONLY);
|
||||
|
||||
// Test redirecting to a trusted URL (same as previous test).
|
||||
// This time we should be unauthorized as Shibboleth is disabled.
|
||||
getClient().perform(get("/api/authn/shibboleth")
|
||||
.param("redirectUrl", "http://localhost:8080/server/api/authn/status")
|
||||
.requestAttr("SHIB-MAIL", eperson.getEmail()))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedirectToAnotherGivenTrustedUrl() throws Exception {
|
||||
String token = getAuthToken(eperson.getEmail(), password);
|
||||
|
||||
getClient(token).perform(get("/api/authn/shibboleth")
|
||||
.param("redirectUrl", "http://anotherdspacehost:4000/home"))
|
||||
getClient().perform(get("/api/authn/shibboleth")
|
||||
.param("redirectUrl", "http://anotherdspacehost:4000/home")
|
||||
.requestAttr("SHIB-MAIL", eperson.getEmail()))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("http://anotherdspacehost:4000/home"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedirectToGivenUntrustedUrl() throws Exception {
|
||||
String token = getAuthToken(eperson.getEmail(), password);
|
||||
// Now attempt to redirect to a URL that is NOT trusted (i.e. not in 'rest.cors.allowed-origins').
|
||||
|
||||
// Now attempt to redirect to a URL that is NOT trusted (i.e. not the Server or UI).
|
||||
// Should result in a 400 error.
|
||||
getClient(token).perform(get("/api/authn/shibboleth")
|
||||
.param("redirectUrl", "http://dspace.org"))
|
||||
.andExpect(status().isBadRequest());
|
||||
getClient().perform(get("/api/authn/shibboleth")
|
||||
.param("redirectUrl", "http://dspace.org")
|
||||
.requestAttr("SHIB-MAIL", eperson.getEmail()))
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedirectRequiresAuth() throws Exception {
|
||||
public void testNoRedirectIfInvalidShibAttributes() throws Exception {
|
||||
// In this request, we use a SHIB-MAIL attribute which does NOT match an EPerson.
|
||||
getClient().perform(get("/api/authn/shibboleth")
|
||||
.requestAttr("SHIB-MAIL", "not-an-eperson@example.com"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedirectRequiresShibAttributes() throws Exception {
|
||||
// Verify this endpoint doesn't work if no SHIB-* attributes are set
|
||||
getClient().perform(get("/api/authn/shibboleth"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedirectRequiresShibAttributes2() throws Exception {
|
||||
String token = getAuthToken(eperson.getEmail(), password);
|
||||
|
||||
// Verify this endpoint also doesn't work using a regular auth token (again if SHIB-* attributes missing)
|
||||
getClient(token).perform(get("/api/authn/shibboleth"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
}
|
||||
|
@@ -179,7 +179,7 @@ public class StatisticsRestRepositoryIT extends AbstractControllerIntegrationTes
|
||||
getClient("unvalidToken").perform(
|
||||
get("/api/statistics/usagereports/" + itemNotVisitedWithBitstreams.getID() + "_" + TOTAL_VISITS_REPORT_ID))
|
||||
// ** THEN **
|
||||
.andExpect(status().isForbidden());
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -829,7 +829,7 @@ public class StatisticsRestRepositoryIT extends AbstractControllerIntegrationTes
|
||||
.perform(get("/api/statistics/usagereports/search/object?uri=http://localhost:8080/server/api/core" +
|
||||
"/items/" + itemNotVisitedWithBitstreams.getID()))
|
||||
// ** THEN **
|
||||
.andExpect(status().isForbidden());
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -49,8 +49,8 @@ public class WorkflowActionRestRepositoryIT extends AbstractControllerIntegratio
|
||||
String token = "nonValidToken";
|
||||
//When we call this facets endpoint
|
||||
getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT))
|
||||
//We expect a 403 Forbidden status
|
||||
.andExpect(status().isForbidden());
|
||||
//We expect a 401 Unauthorized status
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -112,8 +112,8 @@ public class WorkflowActionRestRepositoryIT extends AbstractControllerIntegratio
|
||||
WorkflowActionConfig existentWorkflow = xmlWorkflowFactory.getActionByName(nameActionWithOptions);
|
||||
//When we call this facets endpoint
|
||||
getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT + "/" + nameActionWithOptions))
|
||||
//We expect a 403 Forbidden status
|
||||
.andExpect(status().isForbidden());
|
||||
//We expect a 401 Unauthorized status
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -121,8 +121,8 @@ public class WorkflowDefinitionRestRepositoryIT extends AbstractControllerIntegr
|
||||
String token = "NonValidToken";
|
||||
//When we call this facets endpoint
|
||||
getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT))
|
||||
//We expect a 403 Forbidden status
|
||||
.andExpect(status().isForbidden());
|
||||
//We expect a 401 Unauthorized status
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -192,8 +192,8 @@ public class WorkflowDefinitionRestRepositoryIT extends AbstractControllerIntegr
|
||||
String workflowName = defaultWorkflow.getID();
|
||||
//When we call this facets endpoint
|
||||
getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + workflowName))
|
||||
//We expect a 403 Forbidden status
|
||||
.andExpect(status().isForbidden());
|
||||
//We expect a 401 Unauthorized status
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -402,8 +402,8 @@ public class WorkflowDefinitionRestRepositoryIT extends AbstractControllerIntegr
|
||||
//When we call this facets endpoint
|
||||
getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + defaultWorkflow.getID()
|
||||
+ "/collections"))
|
||||
//We expect a 403 Forbidden status
|
||||
.andExpect(status().isForbidden());
|
||||
//We expect a 401 Unauthorized status
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -441,8 +441,8 @@ public class WorkflowDefinitionRestRepositoryIT extends AbstractControllerIntegr
|
||||
//When we call this facets endpoint
|
||||
getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + defaultWorkflow.getID()
|
||||
+ "/steps"))
|
||||
//We expect a 403 Forbidden status
|
||||
.andExpect(status().isForbidden());
|
||||
//We expect a 401 Unauthorized status
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -47,8 +47,8 @@ public class WorkflowStepRestRepositoryIT extends AbstractControllerIntegrationT
|
||||
String token = "NonValidToken";
|
||||
//When we call this facets endpoint
|
||||
getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT))
|
||||
//We expect a 403 Forbidden status
|
||||
.andExpect(status().isForbidden());
|
||||
//We expect a 401 Unauthorized status
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.rest.authorization;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import org.dspace.app.rest.authorization.impl.CanCreateVersionFeature;
|
||||
import org.dspace.app.rest.converter.ItemConverter;
|
||||
import org.dspace.app.rest.matcher.AuthorizationMatcher;
|
||||
import org.dspace.app.rest.model.ItemRest;
|
||||
import org.dspace.app.rest.projection.DefaultProjection;
|
||||
import org.dspace.app.rest.projection.Projection;
|
||||
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||
import org.dspace.app.rest.utils.Utils;
|
||||
import org.dspace.builder.CollectionBuilder;
|
||||
import org.dspace.builder.CommunityBuilder;
|
||||
import org.dspace.builder.EPersonBuilder;
|
||||
import org.dspace.builder.ItemBuilder;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Test for the canCreateVersion authorization feature.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
|
||||
*/
|
||||
public class CanCreateVersionFeatureIT extends AbstractControllerIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private Utils utils;
|
||||
|
||||
@Autowired
|
||||
private ItemConverter itemConverter;
|
||||
|
||||
@Autowired
|
||||
private ConfigurationService configurationService;
|
||||
|
||||
@Autowired
|
||||
private AuthorizationFeatureService authorizationFeatureService;
|
||||
|
||||
private Item itemA;
|
||||
private Item itemB;
|
||||
private EPerson user;
|
||||
private ItemRest itemARest;
|
||||
private Community communityA;
|
||||
private Collection collectionA;
|
||||
private AuthorizationFeature canCreateVersionFeature;
|
||||
|
||||
final String feature = "canCreateVersion";
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
canCreateVersionFeature = authorizationFeatureService.find(CanCreateVersionFeature.NAME);
|
||||
|
||||
user = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("userEmail@test.com")
|
||||
.withPassword(password).build();
|
||||
|
||||
communityA = CommunityBuilder.createCommunity(context)
|
||||
.withName("communityA").build();
|
||||
|
||||
collectionA = CollectionBuilder.createCollection(context, communityA)
|
||||
.withName("collectionA").build();
|
||||
|
||||
itemA = ItemBuilder.createItem(context, collectionA)
|
||||
.withTitle("Item A").build();
|
||||
|
||||
itemB = ItemBuilder.createItem(context, collectionA)
|
||||
.withTitle("Item B").build();
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
itemARest = itemConverter.convert(itemA, Projection.DEFAULT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anonymousHasNotAccessTest() throws Exception {
|
||||
getClient().perform(get("/api/authz/authorizations/search/object")
|
||||
.param("embed", "feature")
|
||||
.param("feature", feature)
|
||||
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)))
|
||||
.andExpect(jsonPath("$._embedded").doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void epersonHasNotAccessTest() throws Exception {
|
||||
String epersonToken = getAuthToken(eperson.getEmail(), password);
|
||||
getClient(epersonToken).perform(get("/api/authz/authorizations/search/object")
|
||||
.param("embed", "feature")
|
||||
.param("feature", feature)
|
||||
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)))
|
||||
.andExpect(jsonPath("$._embedded").doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adminItemSuccessTest() throws Exception {
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(adminToken).perform(get("/api/authz/authorizations/search/object")
|
||||
.param("embed", "feature")
|
||||
.param("feature", feature)
|
||||
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.page.totalElements", greaterThan(0)))
|
||||
.andExpect(jsonPath("$._embedded").exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void submitterItemSuccessTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
configurationService.setProperty("versioning.submitterCanCreateNewVersion", true);
|
||||
itemA.setSubmitter(user);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String userToken = getAuthToken(user.getEmail(), password);
|
||||
getClient(userToken).perform(get("/api/authz/authorizations/search/object")
|
||||
.param("embed", "feature")
|
||||
.param("feature", feature)
|
||||
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.page.totalElements", greaterThan(0)))
|
||||
.andExpect(jsonPath("$._embedded").exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void submitterItemWithPropertySubmitterCanCreateNewVersionIsFalseTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
configurationService.setProperty("versioning.submitterCanCreateNewVersion", false);
|
||||
itemA.setSubmitter(user);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String userToken = getAuthToken(user.getEmail(), password);
|
||||
getClient(userToken).perform(get("/api/authz/authorizations/search/object")
|
||||
.param("embed", "feature")
|
||||
.param("feature", feature)
|
||||
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)))
|
||||
.andExpect(jsonPath("$._embedded").doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void checkCanCreateVersionsFeatureTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
configurationService.setProperty("versioning.submitterCanCreateNewVersion", true);
|
||||
itemA.setSubmitter(user);
|
||||
itemB.setSubmitter(admin);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
|
||||
ItemRest itemRestB = itemConverter.convert(itemB, DefaultProjection.DEFAULT);
|
||||
|
||||
String tokenEPerson = getAuthToken(eperson.getEmail(), password);
|
||||
String tokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||
String tokenUser = getAuthToken(user.getEmail(), password);
|
||||
|
||||
// define authorizations that we know must exists
|
||||
Authorization admin2ItemA = new Authorization(admin, canCreateVersionFeature, itemRestA);
|
||||
Authorization admin2ItemB = new Authorization(admin, canCreateVersionFeature, itemRestB);
|
||||
Authorization user2ItemA = new Authorization(user, canCreateVersionFeature, itemRestA);
|
||||
|
||||
// define authorization that we know not exists
|
||||
Authorization eperson2ItemA = new Authorization(eperson, canCreateVersionFeature, itemRestA);
|
||||
Authorization eperson2ItemB = new Authorization(eperson, canCreateVersionFeature, itemRestB);
|
||||
Authorization user2ItemB = new Authorization(user, canCreateVersionFeature, itemRestB);
|
||||
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemB.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemB))));
|
||||
|
||||
getClient(tokenUser).perform(get("/api/authz/authorizations/" + user2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(user2ItemA))));
|
||||
|
||||
getClient(tokenEPerson).perform(get("/api/authz/authorizations/" + eperson2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenEPerson).perform(get("/api/authz/authorizations/" + eperson2ItemB.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenUser).perform(get("/api/authz/authorizations/" + user2ItemB.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,547 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.rest.authorization;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import org.dspace.app.rest.authorization.impl.CanManageBitstreamBundlesFeature;
|
||||
import org.dspace.app.rest.converter.ItemConverter;
|
||||
import org.dspace.app.rest.matcher.AuthorizationMatcher;
|
||||
import org.dspace.app.rest.model.ItemRest;
|
||||
import org.dspace.app.rest.projection.DefaultProjection;
|
||||
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.builder.CollectionBuilder;
|
||||
import org.dspace.builder.CommunityBuilder;
|
||||
import org.dspace.builder.EPersonBuilder;
|
||||
import org.dspace.builder.ItemBuilder;
|
||||
import org.dspace.builder.ResourcePolicyBuilder;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Test for the canManageBitstreamBundles authorization feature.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
|
||||
*/
|
||||
public class CanManageBitstreamBundlesFeatureIT extends AbstractControllerIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private ItemConverter itemConverter;
|
||||
|
||||
@Autowired
|
||||
private AuthorizeService authorizeService;
|
||||
|
||||
@Autowired
|
||||
private ConfigurationService configurationService;
|
||||
|
||||
@Autowired
|
||||
private AuthorizationFeatureService authorizationFeatureService;
|
||||
|
||||
private Item itemA;
|
||||
private Item itemB;
|
||||
private EPerson userA;
|
||||
private EPerson userB;
|
||||
private EPerson userColAadmin;
|
||||
private EPerson userColBadmin;
|
||||
private EPerson userComAdmin;
|
||||
private Community communityA;
|
||||
private Collection collectionA;
|
||||
private Collection collectionB;
|
||||
private AuthorizationFeature canManageBitstreamBundlesFeature;
|
||||
|
||||
final String feature = "canManageBitstreamBundles";
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
canManageBitstreamBundlesFeature = authorizationFeatureService.find(CanManageBitstreamBundlesFeature.NAME);
|
||||
|
||||
userA = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("userEmail@test.com")
|
||||
.withPassword(password).build();
|
||||
|
||||
userB = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("userB.email@test.com")
|
||||
.withPassword(password).build();
|
||||
|
||||
userColAadmin = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("userColAadmin@test.com")
|
||||
.withPassword(password).build();
|
||||
|
||||
userColBadmin = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("userColBadmin@test.com")
|
||||
.withPassword(password).build();
|
||||
|
||||
userComAdmin = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("userComAdmin@test.com")
|
||||
.withPassword(password).build();
|
||||
|
||||
communityA = CommunityBuilder.createCommunity(context)
|
||||
.withName("communityA")
|
||||
.withAdminGroup(userComAdmin).build();
|
||||
|
||||
collectionA = CollectionBuilder.createCollection(context, communityA)
|
||||
.withName("Collection A")
|
||||
.withAdminGroup(userColAadmin).build();
|
||||
|
||||
collectionB = CollectionBuilder.createCollection(context, communityA)
|
||||
.withName("Collection B")
|
||||
.withAdminGroup(userColBadmin).build();
|
||||
|
||||
itemA = ItemBuilder.createItem(context, collectionA)
|
||||
.withTitle("Item A").build();
|
||||
|
||||
itemB = ItemBuilder.createItem(context, collectionB)
|
||||
.withTitle("Item B").build();
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void checkCanCreateVersionsFeatureTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
//permissions for userA
|
||||
authorizeService.addPolicy(context, itemA, Constants.ADD, userA);
|
||||
authorizeService.addPolicy(context, itemA, Constants.REMOVE, userA);
|
||||
// permissions for userB
|
||||
authorizeService.addPolicy(context, itemA, Constants.REMOVE, userB);
|
||||
authorizeService.addPolicy(context, itemB, Constants.REMOVE, userB);
|
||||
authorizeService.addPolicy(context, itemB, Constants.ADD, userB);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
|
||||
ItemRest itemRestB = itemConverter.convert(itemB, DefaultProjection.DEFAULT);
|
||||
|
||||
String tokenEPerson = getAuthToken(eperson.getEmail(), password);
|
||||
String tokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||
String tokenAUser = getAuthToken(userA.getEmail(), password);
|
||||
String tokenBUser = getAuthToken(userB.getEmail(), password);
|
||||
String tokenComAdmin = getAuthToken(userComAdmin.getEmail(), password);
|
||||
String tokenColAadmin = getAuthToken(userColAadmin.getEmail(), password);
|
||||
String tokenColBadmin = getAuthToken(userColBadmin.getEmail(), password);
|
||||
|
||||
// define authorizations that we know must exists
|
||||
Authorization admin2ItemA = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization admin2ItemB = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestB);
|
||||
Authorization userA2ItemA = new Authorization(userA, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization userB2ItemB = new Authorization(userB, canManageBitstreamBundlesFeature, itemRestB);
|
||||
Authorization comAdmin2ItemB = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestB);
|
||||
Authorization comAdmin2ItemA = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization colAadmin2ItemA = new Authorization(userColAadmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
|
||||
// define authorization that we know not exists
|
||||
Authorization userB2ItemA = new Authorization(userB, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization userA2ItemB = new Authorization(userA, canManageBitstreamBundlesFeature, itemRestB);
|
||||
Authorization eperson2ItemA = new Authorization(eperson, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization eperson2ItemB = new Authorization(eperson, canManageBitstreamBundlesFeature, itemRestB);
|
||||
Authorization anonymous2ItemA = new Authorization(null, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization anonymous2ItemB = new Authorization(null, canManageBitstreamBundlesFeature, itemRestB);
|
||||
Authorization colAadmin2ItemB = new Authorization(userColAadmin, canManageBitstreamBundlesFeature, itemRestB);
|
||||
Authorization colBadmin2ItemA = new Authorization(userColBadmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemB.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemB))));
|
||||
|
||||
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(userA2ItemA))));
|
||||
|
||||
getClient(tokenBUser).perform(get("/api/authz/authorizations/" + userB2ItemB.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(userB2ItemB))));
|
||||
|
||||
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemB.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemB))));
|
||||
|
||||
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemA))));
|
||||
|
||||
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(colAadmin2ItemA))));
|
||||
|
||||
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemB.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenColBadmin).perform(get("/api/authz/authorizations/" + colBadmin2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenBUser).perform(get("/api/authz/authorizations/" + userB2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemB.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenEPerson).perform(get("/api/authz/authorizations/" + eperson2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenEPerson).perform(get("/api/authz/authorizations/" + eperson2ItemB.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient().perform(get("/api/authz/authorizations/" + anonymous2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient().perform(get("/api/authz/authorizations/" + anonymous2ItemB.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void itemAdminSetPropertyCreateBitstreamToFalseTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
ResourcePolicyBuilder.createResourcePolicy(context)
|
||||
.withAction(Constants.ADMIN)
|
||||
.withUser(userA)
|
||||
.withDspaceObject(itemA).build();
|
||||
|
||||
configurationService.setProperty("core.authorization.item-admin.create-bitstream", false);
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
|
||||
|
||||
String tokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||
String tokenAUser = getAuthToken(userA.getEmail(), password);
|
||||
String tokenComAdmin = getAuthToken(userComAdmin.getEmail(), password);
|
||||
String tokenColAadmin = getAuthToken(userColAadmin.getEmail(), password);
|
||||
|
||||
// define authorizations that we know must exists
|
||||
Authorization admin2ItemA = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization comAdmin2ItemA = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization colAadmin2ItemA = new Authorization(userColAadmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
|
||||
// define authorization that we know not exists
|
||||
Authorization userA2ItemA = new Authorization(userA, canManageBitstreamBundlesFeature, itemRestA);
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
|
||||
|
||||
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemA))));
|
||||
|
||||
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(colAadmin2ItemA))));
|
||||
|
||||
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void itemAdminSetPropertyDeleteBitstreamToFalseTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
ResourcePolicyBuilder.createResourcePolicy(context)
|
||||
.withAction(Constants.ADMIN)
|
||||
.withUser(userA)
|
||||
.withDspaceObject(itemA).build();
|
||||
|
||||
configurationService.setProperty("core.authorization.item-admin.delete-bitstream", false);
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
|
||||
|
||||
String tokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||
String tokenAUser = getAuthToken(userA.getEmail(), password);
|
||||
String tokenComAdmin = getAuthToken(userComAdmin.getEmail(), password);
|
||||
String tokenColAadmin = getAuthToken(userColAadmin.getEmail(), password);
|
||||
|
||||
// define authorizations that we know must exists
|
||||
Authorization admin2ItemA = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization comAdmin2ItemA = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization colAadmin2ItemA = new Authorization(userColAadmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
|
||||
// define authorization that we know not exists
|
||||
Authorization userA2ItemA = new Authorization(userA, canManageBitstreamBundlesFeature, itemRestA);
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
|
||||
|
||||
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemA))));
|
||||
|
||||
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(colAadmin2ItemA))));
|
||||
|
||||
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void itemAdminSetPropertyCollectionAdminCreateBitstreamToFalseTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
ResourcePolicyBuilder.createResourcePolicy(context)
|
||||
.withAction(Constants.ADMIN)
|
||||
.withUser(userA)
|
||||
.withDspaceObject(itemA).build();
|
||||
|
||||
configurationService.setProperty("core.authorization.collection-admin.item.create-bitstream", false);
|
||||
configurationService.setProperty("core.authorization.item-admin.delete-bitstream", false);
|
||||
configurationService.setProperty("core.authorization.item-admin.create-bitstream", false);
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
|
||||
ItemRest itemRestB = itemConverter.convert(itemB, DefaultProjection.DEFAULT);
|
||||
|
||||
String tokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||
String tokenAUser = getAuthToken(userA.getEmail(), password);
|
||||
String tokenComAdmin = getAuthToken(userComAdmin.getEmail(), password);
|
||||
String tokenColAadmin = getAuthToken(userColAadmin.getEmail(), password);
|
||||
String tokenColBadmin = getAuthToken(userColBadmin.getEmail(), password);
|
||||
|
||||
// define authorizations that we know must exists
|
||||
Authorization admin2ItemA = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization admin2ItemB = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestB);
|
||||
Authorization comAdmin2ItemA = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization comAdmin2ItemB = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestB);
|
||||
|
||||
// define authorization that we know not exists
|
||||
Authorization colAadmin2ItemA = new Authorization(userColAadmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization colBadmin2ItemB = new Authorization(userColBadmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization userA2ItemA = new Authorization(userA, canManageBitstreamBundlesFeature, itemRestA);
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemB.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemB))));
|
||||
|
||||
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemA))));
|
||||
|
||||
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemB.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemB))));
|
||||
|
||||
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenColBadmin).perform(get("/api/authz/authorizations/" + colBadmin2ItemB.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void itemAdminSetPropertyCollectionAdminDeleteBitstreamToFalseTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
ResourcePolicyBuilder.createResourcePolicy(context)
|
||||
.withAction(Constants.ADMIN)
|
||||
.withUser(userA)
|
||||
.withDspaceObject(itemA).build();
|
||||
|
||||
configurationService.setProperty("core.authorization.collection-admin.item.delete-bitstream", false);
|
||||
configurationService.setProperty("core.authorization.item-admin.delete-bitstream", false);
|
||||
configurationService.setProperty("core.authorization.item-admin.create-bitstream", false);
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
|
||||
ItemRest itemRestB = itemConverter.convert(itemB, DefaultProjection.DEFAULT);
|
||||
|
||||
String tokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||
String tokenAUser = getAuthToken(userA.getEmail(), password);
|
||||
String tokenComAdmin = getAuthToken(userComAdmin.getEmail(), password);
|
||||
String tokenColAadmin = getAuthToken(userColAadmin.getEmail(), password);
|
||||
String tokenColBadmin = getAuthToken(userColBadmin.getEmail(), password);
|
||||
|
||||
// define authorizations that we know must exists
|
||||
Authorization admin2ItemA = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization admin2ItemB = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestB);
|
||||
Authorization comAdmin2ItemA = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization comAdmin2ItemB = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestB);
|
||||
|
||||
// define authorization that we know not exists
|
||||
Authorization colAadmin2ItemA = new Authorization(userColAadmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization colBadmin2ItemB = new Authorization(userColBadmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization userA2ItemA = new Authorization(userA, canManageBitstreamBundlesFeature, itemRestA);
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemB.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemB))));
|
||||
|
||||
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemA))));
|
||||
|
||||
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemB.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemB))));
|
||||
|
||||
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenColBadmin).perform(get("/api/authz/authorizations/" + colBadmin2ItemB.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void itemAdminSetPropertyCommunityAdminCreateBitstreamToFalseTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
ResourcePolicyBuilder.createResourcePolicy(context)
|
||||
.withAction(Constants.ADMIN)
|
||||
.withUser(userA)
|
||||
.withDspaceObject(itemA).build();
|
||||
|
||||
configurationService.setProperty("core.authorization.community-admin.item.create-bitstream", false);
|
||||
configurationService.setProperty("core.authorization.collection-admin.item.create-bitstream", false);
|
||||
configurationService.setProperty("core.authorization.collection-admin.item.delete-bitstream", false);
|
||||
configurationService.setProperty("core.authorization.item-admin.delete-bitstream", false);
|
||||
configurationService.setProperty("core.authorization.item-admin.create-bitstream", false);
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
|
||||
ItemRest itemRestB = itemConverter.convert(itemB, DefaultProjection.DEFAULT);
|
||||
|
||||
String tokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||
String tokenAUser = getAuthToken(userA.getEmail(), password);
|
||||
String tokenComAdmin = getAuthToken(userComAdmin.getEmail(), password);
|
||||
String tokenColAadmin = getAuthToken(userColAadmin.getEmail(), password);
|
||||
String tokenColBadmin = getAuthToken(userColBadmin.getEmail(), password);
|
||||
|
||||
// define authorizations that we know must exists
|
||||
Authorization admin2ItemA = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization admin2ItemB = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestB);
|
||||
|
||||
// define authorization that we know not exists
|
||||
Authorization comAdmin2ItemA = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization comAdmin2ItemB = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestB);
|
||||
Authorization colAadmin2ItemA = new Authorization(userColAadmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization colBadmin2ItemB = new Authorization(userColBadmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization userA2ItemA = new Authorization(userA, canManageBitstreamBundlesFeature, itemRestA);
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemB.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemB))));
|
||||
|
||||
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemB.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenColBadmin).perform(get("/api/authz/authorizations/" + colBadmin2ItemB.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void itemAdminSetPropertyCommunityAdminDeleteBitstreamToFalseTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
ResourcePolicyBuilder.createResourcePolicy(context)
|
||||
.withAction(Constants.ADMIN)
|
||||
.withUser(userA)
|
||||
.withDspaceObject(itemA).build();
|
||||
|
||||
configurationService.setProperty("core.authorization.community-admin.item.delete-bitstream", false);
|
||||
configurationService.setProperty("core.authorization.collection-admin.item.create-bitstream", false);
|
||||
configurationService.setProperty("core.authorization.collection-admin.item.delete-bitstream", false);
|
||||
configurationService.setProperty("core.authorization.item-admin.delete-bitstream", false);
|
||||
configurationService.setProperty("core.authorization.item-admin.create-bitstream", false);
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
|
||||
ItemRest itemRestB = itemConverter.convert(itemB, DefaultProjection.DEFAULT);
|
||||
|
||||
String tokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||
String tokenAUser = getAuthToken(userA.getEmail(), password);
|
||||
String tokenComAdmin = getAuthToken(userComAdmin.getEmail(), password);
|
||||
String tokenColAadmin = getAuthToken(userColAadmin.getEmail(), password);
|
||||
String tokenColBadmin = getAuthToken(userColBadmin.getEmail(), password);
|
||||
|
||||
// define authorizations that we know must exists
|
||||
Authorization admin2ItemA = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization admin2ItemB = new Authorization(admin, canManageBitstreamBundlesFeature, itemRestB);
|
||||
|
||||
// define authorization that we know not exists
|
||||
Authorization comAdmin2ItemA = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization comAdmin2ItemB = new Authorization(userComAdmin, canManageBitstreamBundlesFeature, itemRestB);
|
||||
Authorization colAadmin2ItemA = new Authorization(userColAadmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization colBadmin2ItemB = new Authorization(userColBadmin, canManageBitstreamBundlesFeature, itemRestA);
|
||||
Authorization userA2ItemA = new Authorization(userA, canManageBitstreamBundlesFeature, itemRestA);
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemB.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemB))));
|
||||
|
||||
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemB.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenColBadmin).perform(get("/api/authz/authorizations/" + colBadmin2ItemB.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -17,17 +17,24 @@ import java.io.InputStream;
|
||||
|
||||
import org.apache.commons.codec.CharEncoding;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.dspace.app.rest.authorization.impl.CanManageMappingsFeature;
|
||||
import org.dspace.app.rest.converter.BitstreamConverter;
|
||||
import org.dspace.app.rest.converter.CollectionConverter;
|
||||
import org.dspace.app.rest.converter.ItemConverter;
|
||||
import org.dspace.app.rest.matcher.AuthorizationMatcher;
|
||||
import org.dspace.app.rest.model.BitstreamRest;
|
||||
import org.dspace.app.rest.model.CollectionRest;
|
||||
import org.dspace.app.rest.model.ItemRest;
|
||||
import org.dspace.app.rest.projection.DefaultProjection;
|
||||
import org.dspace.app.rest.projection.Projection;
|
||||
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||
import org.dspace.app.rest.utils.Utils;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.builder.BitstreamBuilder;
|
||||
import org.dspace.builder.BundleBuilder;
|
||||
import org.dspace.builder.CollectionBuilder;
|
||||
import org.dspace.builder.CommunityBuilder;
|
||||
import org.dspace.builder.EPersonBuilder;
|
||||
import org.dspace.builder.ItemBuilder;
|
||||
import org.dspace.builder.ResourcePolicyBuilder;
|
||||
import org.dspace.content.Bitstream;
|
||||
@@ -36,14 +43,16 @@ import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Test for the canManageMappedItems authorization feature
|
||||
* Test for the canManageMappings authorization feature.
|
||||
*/
|
||||
public class ManageMappedItemsFeatureIT extends AbstractControllerIntegrationTest {
|
||||
public class CanManageMappingsFeatureIT extends AbstractControllerIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private Utils utils;
|
||||
@@ -54,15 +63,27 @@ public class ManageMappedItemsFeatureIT extends AbstractControllerIntegrationTes
|
||||
@Autowired
|
||||
private BitstreamConverter bitstreamConverter;
|
||||
|
||||
@Autowired
|
||||
private ItemConverter itemConverter;
|
||||
|
||||
@Autowired
|
||||
private AuthorizeService authorizeService;
|
||||
|
||||
@Autowired
|
||||
private AuthorizationFeatureService authorizationFeatureService;
|
||||
|
||||
private EPerson userA;
|
||||
private Community communityA;
|
||||
private Collection collectionA;
|
||||
private Collection collectionB;
|
||||
private CollectionRest collectionARest;
|
||||
private Item itemA;
|
||||
private Bitstream bitstreamA;
|
||||
private BitstreamRest bitstreamARest;
|
||||
private Bundle bundleA;
|
||||
private AuthorizationFeature canManageMappingsFeature;
|
||||
|
||||
final String feature = "canManageMappedItems";
|
||||
final String feature = "canManageMappings";
|
||||
|
||||
@Override
|
||||
@Before
|
||||
@@ -70,12 +91,19 @@ public class ManageMappedItemsFeatureIT extends AbstractControllerIntegrationTes
|
||||
super.setUp();
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
userA = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("userEmail@test.com")
|
||||
.withPassword(password).build();
|
||||
|
||||
communityA = CommunityBuilder.createCommunity(context)
|
||||
.withName("communityA")
|
||||
.build();
|
||||
collectionA = CollectionBuilder.createCollection(context, communityA)
|
||||
.withName("collectionA")
|
||||
.build();
|
||||
collectionB = CollectionBuilder.createCollection(context, communityA)
|
||||
.withName("collectionB")
|
||||
.build();
|
||||
itemA = ItemBuilder.createItem(context, collectionA)
|
||||
.withTitle("itemA")
|
||||
.build();
|
||||
@@ -88,7 +116,7 @@ public class ManageMappedItemsFeatureIT extends AbstractControllerIntegrationTes
|
||||
.withName("bistreamA")
|
||||
.build();
|
||||
}
|
||||
|
||||
canManageMappingsFeature = authorizationFeatureService.find(CanManageMappingsFeature.NAME);
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
collectionARest = collectionConverter.convert(collectionA, Projection.DEFAULT);
|
||||
@@ -188,4 +216,65 @@ public class ManageMappedItemsFeatureIT extends AbstractControllerIntegrationTes
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)))
|
||||
.andExpect(jsonPath("$._embedded").doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void canManageMappingsWithUserThatCanManageTwoCollectionsTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
authorizeService.addPolicy(context, collectionA, Constants.ADD, userA);
|
||||
authorizeService.addPolicy(context, collectionB, Constants.ADD, userA);
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
|
||||
|
||||
String tokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||
String tokenAUser = getAuthToken(userA.getEmail(), password);
|
||||
String tokenEPerson = getAuthToken(eperson.getEmail(), password);
|
||||
|
||||
// define authorizations that we know must exists
|
||||
Authorization admin2ItemA = new Authorization(admin, canManageMappingsFeature, itemRestA);
|
||||
Authorization userA2ItemA = new Authorization(userA, canManageMappingsFeature, itemRestA);
|
||||
|
||||
// define authorization that we know not exists
|
||||
Authorization eperson2ItemA = new Authorization(eperson, canManageMappingsFeature, itemRestA);
|
||||
Authorization anonymous2ItemA = new Authorization(null, canManageMappingsFeature, itemRestA);
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
|
||||
|
||||
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(userA2ItemA))));
|
||||
|
||||
getClient(tokenEPerson).perform(get("/api/authz/authorizations/" + eperson2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient().perform(get("/api/authz/authorizations/" + anonymous2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void canManageMappingsOnlyAdminHasAccessTest() throws Exception {
|
||||
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
|
||||
|
||||
String tokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||
String tokenAUser = getAuthToken(userA.getEmail(), password);
|
||||
|
||||
// define authorizations that we know must exists
|
||||
Authorization admin2ItemA = new Authorization(admin, canManageMappingsFeature, itemRestA);
|
||||
|
||||
// define authorization that we know not exists
|
||||
Authorization userA2ItemA = new Authorization(userA, canManageMappingsFeature, itemRestA);
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
|
||||
|
||||
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.rest.authorization;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import org.dspace.app.rest.authorization.impl.CanManageRelationshipsFeature;
|
||||
import org.dspace.app.rest.converter.ItemConverter;
|
||||
import org.dspace.app.rest.matcher.AuthorizationMatcher;
|
||||
import org.dspace.app.rest.model.ItemRest;
|
||||
import org.dspace.app.rest.projection.DefaultProjection;
|
||||
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.builder.CollectionBuilder;
|
||||
import org.dspace.builder.CommunityBuilder;
|
||||
import org.dspace.builder.EPersonBuilder;
|
||||
import org.dspace.builder.ItemBuilder;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Test for the canManageRelationships authorization feature.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
|
||||
*/
|
||||
public class CanManageRelationshipsFeatureIT extends AbstractControllerIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private ItemConverter itemConverter;
|
||||
|
||||
@Autowired
|
||||
private AuthorizeService authorizeService;
|
||||
|
||||
@Autowired
|
||||
private AuthorizationFeatureService authorizationFeatureService;
|
||||
|
||||
private Item itemA;
|
||||
private Item itemB;
|
||||
private EPerson userA;
|
||||
private EPerson userB;
|
||||
private EPerson userColAadmin;
|
||||
private EPerson userColBadmin;
|
||||
private EPerson userComAdmin;
|
||||
private Community communityA;
|
||||
private Collection collectionA;
|
||||
private Collection collectionB;
|
||||
private AuthorizationFeature canManageRelationshipsFeature;
|
||||
|
||||
final String feature = "canManageRelationships";
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
canManageRelationshipsFeature = authorizationFeatureService.find(CanManageRelationshipsFeature.NAME);
|
||||
|
||||
userA = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("userEmail@test.com")
|
||||
.withPassword(password).build();
|
||||
|
||||
userB = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("userB.email@test.com")
|
||||
.withPassword(password).build();
|
||||
|
||||
userColAadmin = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("userColAadmin@test.com")
|
||||
.withPassword(password).build();
|
||||
|
||||
userColBadmin = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("userColBadmin@test.com")
|
||||
.withPassword(password).build();
|
||||
|
||||
userComAdmin = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("userComAdmin@test.com")
|
||||
.withPassword(password).build();
|
||||
|
||||
communityA = CommunityBuilder.createCommunity(context)
|
||||
.withName("communityA")
|
||||
.withAdminGroup(userComAdmin).build();
|
||||
|
||||
collectionA = CollectionBuilder.createCollection(context, communityA)
|
||||
.withName("Collection A")
|
||||
.withAdminGroup(userColAadmin).build();
|
||||
|
||||
collectionB = CollectionBuilder.createCollection(context, communityA)
|
||||
.withName("Collection B")
|
||||
.withAdminGroup(userColBadmin).build();
|
||||
|
||||
itemA = ItemBuilder.createItem(context, collectionA)
|
||||
.withTitle("Item A").build();
|
||||
|
||||
itemB = ItemBuilder.createItem(context, collectionB)
|
||||
.withTitle("Item B").build();
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void canManageRelationshipsFeatureTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
// permissions for userA
|
||||
authorizeService.addPolicy(context, itemA, Constants.WRITE, userA);
|
||||
// permissions for userB
|
||||
authorizeService.addPolicy(context, itemB, Constants.WRITE, userB);
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
ItemRest itemRestA = itemConverter.convert(itemA, DefaultProjection.DEFAULT);
|
||||
ItemRest itemRestB = itemConverter.convert(itemB, DefaultProjection.DEFAULT);
|
||||
|
||||
String tokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||
String tokenComAdmin = getAuthToken(userComAdmin.getEmail(), password);
|
||||
String tokenColAadmin = getAuthToken(userColAadmin.getEmail(), password);
|
||||
String tokenColBadmin = getAuthToken(userColBadmin.getEmail(), password);
|
||||
String tokenAUser = getAuthToken(userA.getEmail(), password);
|
||||
String tokenBUser = getAuthToken(userB.getEmail(), password);
|
||||
String tokenEPerson = getAuthToken(eperson.getEmail(), password);
|
||||
|
||||
// define authorizations that we know must exists
|
||||
Authorization admin2ItemA = new Authorization(admin, canManageRelationshipsFeature, itemRestA);
|
||||
Authorization admin2ItemB = new Authorization(admin, canManageRelationshipsFeature, itemRestB);
|
||||
Authorization comAdmin2ItemA = new Authorization(userComAdmin, canManageRelationshipsFeature, itemRestA);
|
||||
Authorization comAdmin2ItemB = new Authorization(userComAdmin, canManageRelationshipsFeature, itemRestB);
|
||||
|
||||
Authorization colAadmin2ItemA = new Authorization(userColAadmin, canManageRelationshipsFeature, itemRestA);
|
||||
Authorization colBadmin2ItemB = new Authorization(userColBadmin, canManageRelationshipsFeature, itemRestB);
|
||||
|
||||
Authorization userA2ItemA = new Authorization(userA, canManageRelationshipsFeature, itemRestA);
|
||||
Authorization userB2ItemB = new Authorization(userB, canManageRelationshipsFeature, itemRestB);
|
||||
|
||||
// define authorization that we know not exists
|
||||
Authorization userB2ItemA = new Authorization(userB, canManageRelationshipsFeature, itemRestA);
|
||||
Authorization userA2ItemB = new Authorization(userA, canManageRelationshipsFeature, itemRestB);
|
||||
Authorization eperson2ItemA = new Authorization(eperson, canManageRelationshipsFeature, itemRestA);
|
||||
Authorization eperson2ItemB = new Authorization(eperson, canManageRelationshipsFeature, itemRestB);
|
||||
Authorization anonymous2ItemA = new Authorization(null, canManageRelationshipsFeature, itemRestA);
|
||||
Authorization anonymous2ItemB = new Authorization(null, canManageRelationshipsFeature, itemRestB);
|
||||
Authorization colAadmin2ItemB = new Authorization(userColAadmin, canManageRelationshipsFeature, itemRestB);
|
||||
Authorization colBadmin2ItemA = new Authorization(userColBadmin, canManageRelationshipsFeature, itemRestA);
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemA))));
|
||||
|
||||
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + admin2ItemB.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(admin2ItemB))));
|
||||
|
||||
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemA))));
|
||||
|
||||
getClient(tokenComAdmin).perform(get("/api/authz/authorizations/" + comAdmin2ItemB.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(comAdmin2ItemB))));
|
||||
|
||||
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(colAadmin2ItemA))));
|
||||
|
||||
getClient(tokenColBadmin).perform(get("/api/authz/authorizations/" + colBadmin2ItemB.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(colBadmin2ItemB))));
|
||||
|
||||
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemA.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(userA2ItemA))));
|
||||
|
||||
getClient(tokenBUser).perform(get("/api/authz/authorizations/" + userB2ItemB.getID()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", Matchers.is(AuthorizationMatcher.matchAuthorization(userB2ItemB))));
|
||||
|
||||
getClient(tokenColAadmin).perform(get("/api/authz/authorizations/" + colAadmin2ItemB.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenColBadmin).perform(get("/api/authz/authorizations/" + colBadmin2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenBUser).perform(get("/api/authz/authorizations/" + userB2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenAUser).perform(get("/api/authz/authorizations/" + userA2ItemB.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenEPerson).perform(get("/api/authz/authorizations/" + eperson2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient(tokenEPerson).perform(get("/api/authz/authorizations/" + eperson2ItemB.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient().perform(get("/api/authz/authorizations/" + anonymous2ItemA.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
getClient().perform(get("/api/authz/authorizations/" + anonymous2ItemB.getID()))
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.rest.authorization;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import org.dspace.app.rest.converter.ItemConverter;
|
||||
import org.dspace.app.rest.model.ItemRest;
|
||||
import org.dspace.app.rest.projection.Projection;
|
||||
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||
import org.dspace.app.rest.utils.Utils;
|
||||
import org.dspace.builder.CollectionBuilder;
|
||||
import org.dspace.builder.CommunityBuilder;
|
||||
import org.dspace.builder.EPersonBuilder;
|
||||
import org.dspace.builder.ItemBuilder;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Test for the canManageVersions authorization feature.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it)
|
||||
*/
|
||||
public class CanManageVersionsFeatureIT extends AbstractControllerIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private Utils utils;
|
||||
|
||||
@Autowired
|
||||
private ItemConverter itemConverter;
|
||||
|
||||
@Autowired
|
||||
private ConfigurationService configurationService;
|
||||
|
||||
private Item itemA;
|
||||
private EPerson user;
|
||||
private ItemRest itemARest;
|
||||
private Community communityA;
|
||||
private Collection collectionA;
|
||||
|
||||
final String feature = "canManageVersions";
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
user = EPersonBuilder.createEPerson(context)
|
||||
.withEmail("userEmail@test.com")
|
||||
.withPassword(password).build();
|
||||
|
||||
communityA = CommunityBuilder.createCommunity(context)
|
||||
.withName("communityA").build();
|
||||
|
||||
collectionA = CollectionBuilder.createCollection(context, communityA)
|
||||
.withName("collectionA").build();
|
||||
|
||||
itemA = ItemBuilder.createItem(context, collectionA)
|
||||
.withTitle("itemA").build();
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
itemARest = itemConverter.convert(itemA, Projection.DEFAULT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anonymousHasNotAccessTest() throws Exception {
|
||||
getClient().perform(get("/api/authz/authorizations/search/object")
|
||||
.param("embed", "feature")
|
||||
.param("feature", feature)
|
||||
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)))
|
||||
.andExpect(jsonPath("$._embedded").doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void epersonHasNotAccessTest() throws Exception {
|
||||
String epersonToken = getAuthToken(eperson.getEmail(), password);
|
||||
getClient(epersonToken).perform(get("/api/authz/authorizations/search/object")
|
||||
.param("embed", "feature")
|
||||
.param("feature", feature)
|
||||
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)))
|
||||
.andExpect(jsonPath("$._embedded").doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adminItemSuccessTest() throws Exception {
|
||||
String adminToken = getAuthToken(admin.getEmail(), password);
|
||||
getClient(adminToken).perform(get("/api/authz/authorizations/search/object")
|
||||
.param("embed", "feature")
|
||||
.param("feature", feature)
|
||||
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.page.totalElements", greaterThan(0)))
|
||||
.andExpect(jsonPath("$._embedded").exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void submitterItemSuccessTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
configurationService.setProperty("versioning.submitterCanCreateNewVersion", true);
|
||||
itemA.setSubmitter(user);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String userToken = getAuthToken(user.getEmail(), password);
|
||||
getClient(userToken).perform(get("/api/authz/authorizations/search/object")
|
||||
.param("embed", "feature")
|
||||
.param("feature", feature)
|
||||
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.page.totalElements", greaterThan(0)))
|
||||
.andExpect(jsonPath("$._embedded").exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void submitterItemWithPropertySubmitterCanCreateNewVersionIsFalseTest() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
configurationService.setProperty("versioning.submitterCanCreateNewVersion", false);
|
||||
itemA.setSubmitter(user);
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String userToken = getAuthToken(user.getEmail(), password);
|
||||
getClient(userToken).perform(get("/api/authz/authorizations/search/object")
|
||||
.param("embed", "feature")
|
||||
.param("feature", feature)
|
||||
.param("uri", utils.linkToSingleResource(itemARest, "self").getHref()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.page.totalElements", is(0)))
|
||||
.andExpect(jsonPath("$._embedded").doesNotExist());
|
||||
}
|
||||
|
||||
}
|
@@ -41,9 +41,9 @@ import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Test for the canViewVersions authorization feature
|
||||
* Test for the canSeeVersions authorization feature
|
||||
*/
|
||||
public class ViewVersionsFeatureIT extends AbstractControllerIntegrationTest {
|
||||
public class CanSeeVersionsFeatureIT extends AbstractControllerIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private Utils utils;
|
||||
@@ -68,7 +68,7 @@ public class ViewVersionsFeatureIT extends AbstractControllerIntegrationTest {
|
||||
private BitstreamRest bitstreamARest;
|
||||
private Bundle bundleA;
|
||||
|
||||
final String feature = "canViewVersions";
|
||||
final String feature = "canSeeVersions";
|
||||
|
||||
@Override
|
||||
@Before
|
@@ -99,9 +99,11 @@ public class DiscoverConfigurationConverterTest {
|
||||
DiscoverySortFieldConfiguration discoverySortFieldConfiguration = new DiscoverySortFieldConfiguration();
|
||||
discoverySortFieldConfiguration.setMetadataField("title");
|
||||
discoverySortFieldConfiguration.setType("text");
|
||||
discoverySortFieldConfiguration.setDefaultSortOrder(DiscoverySortFieldConfiguration.SORT_ORDER.asc);
|
||||
DiscoverySortFieldConfiguration discoverySortFieldConfiguration1 = new DiscoverySortFieldConfiguration();
|
||||
discoverySortFieldConfiguration1.setMetadataField("author");
|
||||
discoverySortFieldConfiguration1.setType("text");
|
||||
discoverySortFieldConfiguration1.setDefaultSortOrder(DiscoverySortFieldConfiguration.SORT_ORDER.asc);
|
||||
LinkedList<DiscoverySortFieldConfiguration> mockedList = new LinkedList<>();
|
||||
mockedList.add(discoverySortFieldConfiguration);
|
||||
mockedList.add(discoverySortFieldConfiguration1);
|
||||
|
@@ -48,4 +48,10 @@ public class SortOptionMatcher {
|
||||
);
|
||||
}
|
||||
|
||||
public static Matcher<? super Object> sortOptionMatcher(String name, String sortDirection) {
|
||||
return allOf(
|
||||
hasJsonPath("$.name", is(name)),
|
||||
hasJsonPath("$.sortOrder", is(sortDirection))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -86,7 +86,7 @@ public class EmbeddedPageHeaderTest {
|
||||
|
||||
Map<String,Object> links = embeddedPageHeader.getLinks();
|
||||
// "self" should be same as URL
|
||||
assertEquals(dspaceURL, ((EmbeddedPageHeader.Href) links.get("self")).getHref());
|
||||
assertEquals(dspaceURL + "?size=10", ((EmbeddedPageHeader.Href) links.get("self")).getHref());
|
||||
// "first" should not exist, as we are on the first page.
|
||||
assertFalse(links.containsKey("first"));
|
||||
// "prev" should not exist, as we are on the first page.
|
||||
|
@@ -28,6 +28,7 @@ import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
@@ -35,6 +36,7 @@ import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.dspace.app.rest.exception.DSpaceBadRequestException;
|
||||
import org.dspace.app.rest.exception.InvalidSearchRequestException;
|
||||
import org.dspace.app.rest.parameter.SearchFilter;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.discovery.DiscoverFacetField;
|
||||
@@ -111,7 +113,8 @@ public class DiscoverQueryBuilderTest {
|
||||
any(), any(DiscoverQuery.class)))
|
||||
.then(invocation -> new FacetYearRange((DiscoverySearchFilterFacet) invocation.getArguments()[2]));
|
||||
|
||||
when(searchService.toFilterQuery(any(Context.class), any(String.class), any(String.class), any(String.class)))
|
||||
when(searchService.toFilterQuery(any(Context.class), any(String.class), any(String.class), any(String.class),
|
||||
any(DiscoveryConfiguration.class)))
|
||||
.then(invocation -> new DiscoverFilterQuery((String) invocation.getArguments()[1],
|
||||
invocation.getArguments()[1] + ":\"" + invocation.getArguments()[3] + "\"",
|
||||
(String) invocation.getArguments()[3]));
|
||||
@@ -139,17 +142,23 @@ public class DiscoverQueryBuilderTest {
|
||||
discoveryConfiguration.setHitHighlightingConfiguration(discoveryHitHighlightingConfiguration);
|
||||
|
||||
|
||||
DiscoverySortConfiguration sortConfiguration = new DiscoverySortConfiguration();
|
||||
|
||||
DiscoverySortFieldConfiguration defaultSort = new DiscoverySortFieldConfiguration();
|
||||
defaultSort.setMetadataField("dc.date.accessioned");
|
||||
defaultSort.setType(DiscoveryConfigurationParameters.TYPE_DATE);
|
||||
sortConfiguration.setDefaultSort(defaultSort);
|
||||
sortConfiguration.setDefaultSortOrder(DiscoverySortConfiguration.SORT_ORDER.desc);
|
||||
defaultSort.setDefaultSortOrder(DiscoverySortFieldConfiguration.SORT_ORDER.desc);
|
||||
|
||||
|
||||
List<DiscoverySortFieldConfiguration> listSortField = new ArrayList<DiscoverySortFieldConfiguration>();
|
||||
listSortField.add(defaultSort);
|
||||
|
||||
DiscoverySortConfiguration sortConfiguration = new DiscoverySortConfiguration();
|
||||
|
||||
DiscoverySortFieldConfiguration titleSort = new DiscoverySortFieldConfiguration();
|
||||
titleSort.setMetadataField("dc.title");
|
||||
sortConfiguration.setSortFields(Arrays.asList(titleSort));
|
||||
titleSort.setDefaultSortOrder(DiscoverySortFieldConfiguration.SORT_ORDER.asc);
|
||||
listSortField.add(titleSort);
|
||||
|
||||
sortConfiguration.setSortFields(listSortField);
|
||||
|
||||
discoveryConfiguration.setSearchSortConfiguration(sortConfiguration);
|
||||
|
||||
@@ -266,7 +275,7 @@ public class DiscoverQueryBuilderTest {
|
||||
.buildQuery(context, scope, discoveryConfiguration, query, Arrays.asList(searchFilter), "TEST", page);
|
||||
}
|
||||
|
||||
@Test(expected = DSpaceBadRequestException.class)
|
||||
@Test(expected = InvalidSearchRequestException.class)
|
||||
public void testInvalidSortField() throws Exception {
|
||||
page = PageRequest.of(2, 10, Sort.Direction.ASC, "test");
|
||||
queryBuilder
|
||||
@@ -283,7 +292,8 @@ public class DiscoverQueryBuilderTest {
|
||||
|
||||
@Test(expected = DSpaceBadRequestException.class)
|
||||
public void testInvalidSearchFilter2() throws Exception {
|
||||
when(searchService.toFilterQuery(any(Context.class), any(String.class), any(String.class), any(String.class)))
|
||||
when(searchService.toFilterQuery(any(Context.class), any(String.class), any(String.class), any(String.class),
|
||||
any(DiscoveryConfiguration.class)))
|
||||
.thenThrow(SQLException.class);
|
||||
|
||||
queryBuilder
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-parent</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -426,10 +426,9 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
|
||||
service = (T) applicationContext.getBean(name, type);
|
||||
} catch (BeansException e) {
|
||||
// no luck, try the fall back option
|
||||
log.info(
|
||||
log.warn(
|
||||
"Unable to locate bean by name or id={}."
|
||||
+ " Will try to look up bean by type next."
|
||||
+ " BeansException: {}", name, e.getMessage());
|
||||
+ " Will try to look up bean by type next.", name, e);
|
||||
service = null;
|
||||
}
|
||||
} else {
|
||||
@@ -438,9 +437,8 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
|
||||
service = (T) applicationContext.getBean(type.getName(), type);
|
||||
} catch (BeansException e) {
|
||||
// no luck, try the fall back option
|
||||
log.info("Unable to locate bean by name or id={}."
|
||||
+ " Will try to look up bean by type next."
|
||||
+ " BeansException: {}", type.getName(), e.getMessage());
|
||||
log.warn("Unable to locate bean by name or id={}."
|
||||
+ " Will try to look up bean by type next.", type.getName(), e);
|
||||
service = null;
|
||||
}
|
||||
}
|
||||
|
@@ -15,7 +15,7 @@
|
||||
<parent>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-parent</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@@ -13,7 +13,7 @@
|
||||
<parent>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-parent</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@@ -20,11 +20,15 @@
|
||||
dspace.dir = /dspace
|
||||
|
||||
# URL of DSpace backend ('server' webapp). Include port number etc.
|
||||
# This is where REST API and all enabled server modules (OAI-PMH, SWORD, SWORDv2, RDF, etc) will respond
|
||||
# DO NOT end it with '/'.
|
||||
# This is where REST API and all enabled server modules (OAI-PMH, SWORD,
|
||||
# SWORDv2, RDF, etc) will respond.
|
||||
dspace.server.url = http://localhost:8080/server
|
||||
|
||||
# URL of DSpace frontend (Angular UI). Include port number etc
|
||||
# This is used by the backend to provide links in emails, RSS feeds, Sitemaps, etc.
|
||||
# URL of DSpace frontend (Angular UI). Include port number etc.
|
||||
# DO NOT end it with '/'.
|
||||
# This is used by the backend to provide links in emails, RSS feeds, Sitemaps,
|
||||
# etc.
|
||||
dspace.ui.url = http://localhost:4000
|
||||
|
||||
# Name of the site
|
||||
@@ -120,8 +124,8 @@ db.removeabandonedtimeout = 300
|
||||
mail.server = smtp.example.com
|
||||
|
||||
# SMTP mail server authentication username and password (if required)
|
||||
mail.server.username =
|
||||
mail.server.password =
|
||||
#mail.server.username =
|
||||
#mail.server.password =
|
||||
|
||||
# SMTP mail server alternate port (defaults to 25)
|
||||
mail.server.port = 25
|
||||
@@ -371,16 +375,6 @@ useProxies = true
|
||||
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
|
||||
#proxies.trusted.include_ui_ip = true
|
||||
|
||||
# Spring Boot proxy configuration (can be set in local.cfg or in application.properties).
|
||||
# By default, Spring Boot does not automatically use X-Forwarded-* Headers when generating links (and similar) in the
|
||||
# REST API. When using a proxy in front of the REST API, you may need to modify this setting:
|
||||
# * NATIVE = allows your web server to natively support standard Forwarded headers
|
||||
# * FRAMEWORK = enables Spring Framework's built in filter to manage these headers in Spring Boot
|
||||
# * NONE = default value. Forwarded headers are ignored
|
||||
# For more information see
|
||||
# https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-use-behind-a-proxy-server
|
||||
#server.forward-headers-strategy=FRAMEWORK
|
||||
|
||||
#### Media Filter / Format Filter plugins (through PluginService) ####
|
||||
# Media/Format Filters help to full-text index content or
|
||||
# perform automated format conversions
|
||||
|
@@ -89,7 +89,7 @@
|
||||
<min>0</min>
|
||||
</leftCardinality>
|
||||
<rightCardinality>
|
||||
<min>1</min>
|
||||
<min>0</min>
|
||||
</rightCardinality>
|
||||
</type>
|
||||
<type>
|
||||
@@ -101,8 +101,7 @@
|
||||
<min>0</min>
|
||||
</leftCardinality>
|
||||
<rightCardinality>
|
||||
<min>1</min>
|
||||
<max>1</max>
|
||||
<min>0</min>
|
||||
</rightCardinality>
|
||||
</type>
|
||||
<type>
|
||||
@@ -128,7 +127,6 @@
|
||||
</leftCardinality>
|
||||
<rightCardinality>
|
||||
<min>0</min>
|
||||
<max>1</max>
|
||||
</rightCardinality>
|
||||
<copyToRight>true</copyToRight>
|
||||
</type>
|
||||
|
@@ -17,7 +17,36 @@
|
||||
<!-- which does not appear in this map will be associated with the mapping -->
|
||||
<!-- for handle "default". -->
|
||||
<submission-map>
|
||||
<!-- Default submission process -->
|
||||
<name-map collection-handle="default" submission-name="traditional"/>
|
||||
|
||||
<!-- Sample Entities Collection configuration based on the demo Entities dataset available at:
|
||||
https://github.com/DSpace-Labs/AIP-Files/releases/tag/demo-entities-data
|
||||
This sets up the following Entity-based Collections in that dataset:
|
||||
"Publication" Collections = "Publications" -> "Articles", "Books", "Publications 2", & "Theses"
|
||||
"OrgUnit" Collection = "Related Objects" -> "OrgUnits"
|
||||
"Person" Collections = "Related Objects" -> "People" & "People 2"
|
||||
"Project" Collections = "Related Objects" -> "Projects" & "Projects 2"
|
||||
"Journal" Collection = "Compound Journals" -> "Journals"
|
||||
"Journal Volume" Collection = "Compound Journals" -> "Journal Volumes"
|
||||
"Journal Issue" Collection = "Compound Journals" -> "Journal Issues"
|
||||
If you are using that demo dataset, you can simply uncomment the below configuration to enable Entity
|
||||
submission into all of the above Collections.
|
||||
-->
|
||||
<!--
|
||||
<name-map collection-handle="123456789/3" submission-name="Publication"/>
|
||||
<name-map collection-handle="123456789/4" submission-name="Publication"/>
|
||||
<name-map collection-handle="123456789/281" submission-name="Publication"/>
|
||||
<name-map collection-handle="123456789/5" submission-name="Publication"/>
|
||||
<name-map collection-handle="123456789/8" submission-name="OrgUnit"/>
|
||||
<name-map collection-handle="123456789/6" submission-name="Person"/>
|
||||
<name-map collection-handle="123456789/279" submission-name="Person"/>
|
||||
<name-map collection-handle="123456789/7" submission-name="Project"/>
|
||||
<name-map collection-handle="123456789/280" submission-name="Project"/>
|
||||
<name-map collection-handle="123456789/28" submission-name="Journal"/>
|
||||
<name-map collection-handle="123456789/29" submission-name="JournalVolume"/>
|
||||
<name-map collection-handle="123456789/30" submission-name="JournalIssue"/>
|
||||
-->
|
||||
</submission-map>
|
||||
|
||||
|
||||
@@ -44,7 +73,7 @@
|
||||
<!-- -->
|
||||
<step-definitions>
|
||||
<!-- The "collection" step is a "special step" which is *REQUIRED* -->
|
||||
<!-- In DSpace, all submitted items must be immediately assigned -->
|
||||
<!-- In DSpace, all submitted items must be immediately assigned -->
|
||||
<!-- to a collection. This step ensures that a collection is always selected. -->
|
||||
<step-definition id="collection">
|
||||
<heading></heading>
|
||||
@@ -52,6 +81,9 @@
|
||||
<type>collection</type>
|
||||
<scope visibility="hidden" visibilityOutside="hidden">submission</scope>
|
||||
</step-definition>
|
||||
|
||||
<!-- The following set of DescribeStep <step-definition>s all point to forms (of the same name) which are
|
||||
defined in submission-forms.xml -->
|
||||
<step-definition id="traditionalpageone" mandatory="true">
|
||||
<heading>submit.progressbar.describe.stepone</heading>
|
||||
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
||||
@@ -62,8 +94,12 @@
|
||||
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
||||
<type>submission-form</type>
|
||||
</step-definition>
|
||||
|
||||
<step-definition id="peopleStep" mandatory="true">
|
||||
<step-definition id="publicationStep" mandatory="true">
|
||||
<heading>submit.progressbar.describe.stepone</heading>
|
||||
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
||||
<type>submission-form</type>
|
||||
</step-definition>
|
||||
<step-definition id="personStep" mandatory="true">
|
||||
<heading>submit.progressbar.describe.stepone</heading>
|
||||
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
||||
<type>submission-form</type>
|
||||
@@ -106,38 +142,26 @@
|
||||
<scope visibilityOutside="read-only">submission</scope>
|
||||
</step-definition>
|
||||
|
||||
<!-- Step Upload Item with Embargo Features to enable this step, please
|
||||
make sure to comment-out the previous step "UploadStep" <step-definition id="upload-with-embargo">
|
||||
<heading>submit.progressbar.upload</heading> <processing-class>org.dspace.submit.step.UploadWithEmbargoStep</processing-class>
|
||||
<type>uploadWithEmbargo</type> </step-definition> -->
|
||||
|
||||
|
||||
<!--Step will be to select a Creative Commons License -->
|
||||
<!-- Uncomment this step to allow the user to select a Creative Commons
|
||||
license -->
|
||||
<!-- <step-definition id="cclicense"> <heading>submit.progressbar.CClicense</heading>
|
||||
<!-- This optional step may allow the user to select a Creative Commons license -->
|
||||
<step-definition id="cclicense" mandatory="false">
|
||||
<heading>submit.progressbar.CClicense</heading>
|
||||
<processing-class>org.dspace.app.rest.submit.step.CCLicenseStep</processing-class>
|
||||
<type>cclicense</type> </step-definition> -->
|
||||
<type>cclicense</type>
|
||||
</step-definition>
|
||||
|
||||
<!--Step will be to enrich the current submission querying external providers or processing the uploaded file -->
|
||||
<!-- Uncomment this step to enrich the current submission using information extracted
|
||||
from uploaded files or metadata. Please note that this step will be triggered only when a request is performed that mean
|
||||
the file to be uploaded or the metadata saved. Angular allows to configure an autosave feature based on a timer or the input
|
||||
of specific metadata such as identifiers used by this step, see the submission.autosave settings in the environment.common.ts
|
||||
Check also config/spring/api/step-processing-listener.xml for further server side configuration
|
||||
<!-- This optional step may enrich the current submission using information extracted
|
||||
from uploaded files or metadata. -->
|
||||
<!-- Please note that this step will be triggered only when a request is performed,
|
||||
e.g. when a file is uploaded or the form is saved. The Angular UI also supports an
|
||||
autosave feature based on a timer, or the input of specific metadata such as identifiers,
|
||||
see the 'submission.autosave' settings in the 'environment.common.ts'.
|
||||
See also 'config/spring/api/step-processing-listener.xml' for further server side configuration
|
||||
-->
|
||||
<!-- <step-definition id="extractionstep">
|
||||
<step-definition id="extractionstep">
|
||||
<heading>submit.progressbar.ExtractMetadataStep</heading>
|
||||
<processing-class>org.dspace.app.rest.submit.step.ExtractMetadataStep</processing-class>
|
||||
<type>extract</type>
|
||||
</step-definition> -->
|
||||
|
||||
<!-- Fake Steps to test parsing of all options -->
|
||||
<!-- <step-definition mandatory="false"> <heading>fake.submission.readonly</heading>
|
||||
<processing-class>org.dspace.submit.step.SampleStep</processing-class> <type>sample</type>
|
||||
<scope visibility="read-only">submission</scope> </step-definition> <step-definition mandatory="false">
|
||||
<heading>fake.workflow.readonly</heading> <processing-class>org.dspace.submit.step.SampleStep</processing-class>
|
||||
<type>sample</type> <scope visibility="read-only">workflow</scope> </step-definition> -->
|
||||
<type>extract</type>
|
||||
</step-definition>
|
||||
|
||||
<!-- OpenAIRE submission steps/forms -->
|
||||
<step-definition id="openAIREProjectForm" mandatory="true">
|
||||
@@ -166,7 +190,7 @@
|
||||
<type>submission-form</type>
|
||||
</step-definition>
|
||||
|
||||
<!-- This is the Sample Step which utilizes the JSPSampleStep class -->
|
||||
<!-- This is the Sample/Fake Step which is used for testing only -->
|
||||
<step-definition id="sample">
|
||||
<heading>Sample</heading>
|
||||
<processing-class>org.dspace.submit.step.SampleStep</processing-class>
|
||||
@@ -192,7 +216,6 @@
|
||||
|
||||
<!--This "traditional" process defines the DEFAULT item submission process -->
|
||||
<submission-process name="traditional">
|
||||
|
||||
<!--Uncommment to display the SAMPLE step as your first step -->
|
||||
<!--<step id="sample"/> -->
|
||||
|
||||
@@ -204,26 +227,45 @@
|
||||
|
||||
<!--Step will be to Upload the item -->
|
||||
<step id="upload"/>
|
||||
<!-- <step id="upload-with-embargo"/> -->
|
||||
<!-- <step id="extractionstep"/> -->
|
||||
|
||||
<!--Step will be to select a Creative Commons License -->
|
||||
<!-- Uncomment this step to allow the user to select a Creative Commons -->
|
||||
<!-- Uncomment this step to allow the user to select a Creative Commons License -->
|
||||
<!-- <step id="cclicense"/> -->
|
||||
|
||||
<!--Step will be to Sign off on the License -->
|
||||
<!--Step will be to Sign off on the required DSpace License agreement -->
|
||||
<step id="license"/>
|
||||
<!-- <step id="creative-commons"/> -->
|
||||
<!-- <step id="verify"/> -->
|
||||
</submission-process>
|
||||
|
||||
<submission-process name="People">
|
||||
<!--
|
||||
Submission Process for 'Publication' Entities.
|
||||
These are not to be confused with default Items, as Publications may be linked with other Entities
|
||||
(e.g. Person, JournalIssue, etc).
|
||||
To enable: create a collection for Publications and add it to the <submission-map> tag linking to this process
|
||||
-->
|
||||
<submission-process name="Publication">
|
||||
<step id="collection"/>
|
||||
<step id="peopleStep"/>
|
||||
<!-- Publications use a custom first step, but share page 2 with "traditional" Item form -->
|
||||
<step id="publicationStep"/>
|
||||
<step id="traditionalpagetwo"/>
|
||||
<step id="upload"/>
|
||||
<step id="license"/>
|
||||
</submission-process>
|
||||
|
||||
<!--
|
||||
Submission Process for 'Person' Entities
|
||||
To enable: create a collection for Persons and add it to the <submission-map> tag linking to this process
|
||||
-->
|
||||
<submission-process name="Person">
|
||||
<step id="collection"/>
|
||||
<step id="personStep"/>
|
||||
<step id="upload"/>
|
||||
<step id="license"/>
|
||||
</submission-process>
|
||||
|
||||
<!--
|
||||
Submission Process for 'Pproject' Entities
|
||||
To enable: create a collection for Projects and add it to the <submission-map> tag linking to this process
|
||||
-->
|
||||
<submission-process name="Project">
|
||||
<step id="collection"/>
|
||||
<step id="projectStep"/>
|
||||
@@ -231,25 +273,44 @@
|
||||
<step id="license"/>
|
||||
</submission-process>
|
||||
|
||||
<!--
|
||||
Submission Process for 'OrgUnit' Entities
|
||||
To enable: create a collection for OrgUnits and add it to the <submission-map> tag linking to this process
|
||||
-->
|
||||
<submission-process name="OrgUnit">
|
||||
<step id="collection"/>
|
||||
<step id="orgUnitStep"/>
|
||||
<step id="upload"/>
|
||||
<step id="license"/>
|
||||
</submission-process>
|
||||
<submission-process name="Journals">
|
||||
|
||||
<!--
|
||||
Submission Process for 'Journal' Entities
|
||||
To enable: create a collection for Journals and add it to the <submission-map> tag linking to this process
|
||||
-->
|
||||
<submission-process name="Journal">
|
||||
<step id="collection"/>
|
||||
<step id="journalStep"/>
|
||||
<step id="upload"/>
|
||||
<step id="license"/>
|
||||
</submission-process>
|
||||
<submission-process name="JournalVolumes">
|
||||
|
||||
<!--
|
||||
Submission Process for 'JournalVolume' Entities
|
||||
To enable: create a collection for JournalVolumes and add it to the <submission-map> tag linking to this process
|
||||
-->
|
||||
<submission-process name="JournalVolume">
|
||||
<step id="collection"/>
|
||||
<step id="journalVolumeStep"/>
|
||||
<step id="upload"/>
|
||||
<step id="license"/>
|
||||
</submission-process>
|
||||
<submission-process name="JournalIssues">
|
||||
|
||||
<!--
|
||||
Submission Process for 'JournalIssues' Entities
|
||||
To enable: create a collection for JournalIssues and add it to the <submission-map> tag linking to this process
|
||||
-->
|
||||
<submission-process name="JournalIssue">
|
||||
<step id="collection"/>
|
||||
<step id="journalIssueStep"/>
|
||||
<step id="upload"/>
|
||||
|
@@ -33,11 +33,15 @@
|
||||
dspace.dir=/dspace
|
||||
|
||||
# URL of DSpace backend ('server' webapp). Include port number etc.
|
||||
# This is where REST API and all enabled server modules (OAI-PMH, SWORD, SWORDv2, RDF, etc) will respond
|
||||
# DO NOT end it with '/'.
|
||||
# This is where REST API and all enabled server modules (OAI-PMH, SWORD,
|
||||
# SWORDv2, RDF, etc) will respond.
|
||||
dspace.server.url = http://localhost:8080/server
|
||||
|
||||
# URL of DSpace frontend (Angular UI). Include port number etc
|
||||
# This is used by the backend to provide links in emails, RSS feeds, Sitemaps, etc.
|
||||
# URL of DSpace frontend (Angular UI). Include port number etc.
|
||||
# DO NOT end it with '/'.
|
||||
# This is used by the backend to provide links in emails, RSS feeds, Sitemaps,
|
||||
# etc.
|
||||
dspace.ui.url = http://localhost:4000
|
||||
|
||||
# Name of the site
|
||||
@@ -188,6 +192,7 @@ db.schema = public
|
||||
#plugin.sequence.org.dspace.authenticate.AuthenticationMethod = org.dspace.authenticate.LDAPAuthentication
|
||||
|
||||
# Shibboleth authentication/authorization. See authentication-shibboleth.cfg for default configuration.
|
||||
# Check also the cors settings below
|
||||
#plugin.sequence.org.dspace.authenticate.AuthenticationMethod = org.dspace.authenticate.ShibAuthentication
|
||||
|
||||
# X.509 certificate authentication. See authentication-x509.cfg for default configuration.
|
||||
@@ -205,6 +210,9 @@ db.schema = public
|
||||
# Defaults to ${dspace.ui.url} if unspecified (as the UI must have access to the REST API).
|
||||
# Multiple allowed origin URLs may be comma separated. Wildcard value (*) is NOT SUPPORTED.
|
||||
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
|
||||
# When an external authentication system is involved like Shibboleth some browsers (i.e. Safari) include
|
||||
# in the request the Origin header with the url of the IdP. In such case you need to allow also the IdP to
|
||||
# avoid trouble for such browsers (i.e. rest.cors.allowed-origins = ${dspace.ui.url}, https://samltest.id )
|
||||
#rest.cors.allowed-origins = ${dspace.ui.url}
|
||||
|
||||
#################################################
|
||||
|
@@ -82,7 +82,7 @@
|
||||
<Logger name='org.dspace.services'
|
||||
level='ERROR'/>
|
||||
<Logger name='org.dspace.servicemanager'
|
||||
level='ERROR'/>
|
||||
level='WARN'/>
|
||||
<Logger name='org.dspace.providers'
|
||||
level='ERROR'/>
|
||||
<Logger name='org.dspace.utils'
|
||||
|
@@ -22,6 +22,7 @@
|
||||
<bean class="org.dspace.app.util.MetadataExposureServiceImpl"/>
|
||||
<bean class="org.dspace.app.util.OpenSearchServiceImpl"/>
|
||||
<bean class="org.dspace.app.util.WebAppServiceImpl"/>
|
||||
<bean class="org.dspace.app.util.DSpaceObjectUtilsImpl"/>
|
||||
|
||||
<bean class="org.dspace.authenticate.AuthenticationServiceImpl"/>
|
||||
|
||||
|
@@ -154,11 +154,9 @@
|
||||
<!--The sort filters for the discovery search-->
|
||||
<property name="searchSortConfiguration">
|
||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
||||
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
|
||||
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
<property name="sortFields">
|
||||
<list>
|
||||
<ref bean="sortScore" />
|
||||
<ref bean="sortTitle" />
|
||||
<ref bean="sortDateIssued" />
|
||||
<ref bean="sortDateAccessioned"/>
|
||||
@@ -295,11 +293,9 @@
|
||||
<!--The sort filters for the discovery search-->
|
||||
<property name="searchSortConfiguration">
|
||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
||||
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
|
||||
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
<property name="sortFields">
|
||||
<list>
|
||||
<ref bean="sortScore" />
|
||||
<ref bean="sortTitle" />
|
||||
<ref bean="sortDateIssued" />
|
||||
<ref bean="sortDateAccessioned"/>
|
||||
@@ -441,11 +437,9 @@
|
||||
<!--The sort filters for the discovery search-->
|
||||
<property name="searchSortConfiguration">
|
||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
||||
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
|
||||
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
<property name="sortFields">
|
||||
<list>
|
||||
<ref bean="sortScore" />
|
||||
<ref bean="sortTitle" />
|
||||
<ref bean="sortDateIssued" />
|
||||
<ref bean="sortDateAccessioned"/>
|
||||
@@ -581,11 +575,9 @@
|
||||
<!--The sort filters for the discovery search (same as defaultConfiguration above)-->
|
||||
<property name="searchSortConfiguration">
|
||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
||||
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
|
||||
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
<property name="sortFields">
|
||||
<list>
|
||||
<ref bean="sortScore" />
|
||||
<ref bean="sortTitle" />
|
||||
<ref bean="sortDateIssued" />
|
||||
<ref bean="sortDateAccessioned" />
|
||||
@@ -687,11 +679,9 @@
|
||||
<!--The sort filters for the discovery search-->
|
||||
<property name="searchSortConfiguration">
|
||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
||||
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
|
||||
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
<property name="sortFields">
|
||||
<list>
|
||||
<ref bean="sortScore" />
|
||||
<ref bean="sortTitle" />
|
||||
<ref bean="sortDateIssued" />
|
||||
</list>
|
||||
@@ -764,11 +754,9 @@
|
||||
<!--The sort filters for the discovery search-->
|
||||
<property name="searchSortConfiguration">
|
||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
||||
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
|
||||
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
<property name="sortFields">
|
||||
<list>
|
||||
<ref bean="sortScore" />
|
||||
<ref bean="sortTitle" />
|
||||
<ref bean="sortDateIssued" />
|
||||
</list>
|
||||
@@ -841,11 +829,9 @@
|
||||
<!--The sort filters for the discovery search-->
|
||||
<property name="searchSortConfiguration">
|
||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
||||
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
|
||||
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
<property name="sortFields">
|
||||
<list>
|
||||
<ref bean="sortScore" />
|
||||
<ref bean="sortTitle" />
|
||||
<ref bean="sortDateIssued" />
|
||||
</list>
|
||||
@@ -927,11 +913,9 @@
|
||||
<!--The sort filters for the discovery search-->
|
||||
<property name="searchSortConfiguration">
|
||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
||||
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
|
||||
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
<property name="sortFields">
|
||||
<list>
|
||||
<ref bean="sortScore" />
|
||||
<ref bean="sortTitle" />
|
||||
<ref bean="sortDateIssued" />
|
||||
<ref bean="sortDateAccessioned"/>
|
||||
@@ -989,11 +973,9 @@
|
||||
<!--The sort filters for the discovery search-->
|
||||
<property name="searchSortConfiguration">
|
||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
||||
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
|
||||
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
<property name="sortFields">
|
||||
<list>
|
||||
<ref bean="sortScore" />
|
||||
<ref bean="sortFamilyName"/>
|
||||
<ref bean="sortGivenName"/>
|
||||
<ref bean="sortBirthDate"/>
|
||||
@@ -1047,11 +1029,9 @@
|
||||
<!--The sort filters for the discovery search-->
|
||||
<property name="searchSortConfiguration">
|
||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
||||
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
|
||||
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
<property name="sortFields">
|
||||
<list>
|
||||
<ref bean="sortScore" />
|
||||
<ref bean="sortTitle"/>
|
||||
</list>
|
||||
</property>
|
||||
@@ -1107,11 +1087,9 @@
|
||||
<!--The sort filters for the discovery search-->
|
||||
<property name="searchSortConfiguration">
|
||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
||||
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
|
||||
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
<property name="sortFields">
|
||||
<list>
|
||||
<ref bean="sortScore" />
|
||||
<ref bean="sortOrganizationLegalName"/>
|
||||
<ref bean="sortOrganizationAddressCountry"/>
|
||||
<ref bean="sortOrganizationAddressLocality"/>
|
||||
@@ -1169,11 +1147,9 @@
|
||||
<!--The sort filters for the discovery search-->
|
||||
<property name="searchSortConfiguration">
|
||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
||||
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
|
||||
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
<property name="sortFields">
|
||||
<list>
|
||||
<ref bean="sortScore" />
|
||||
<ref bean="sortPublicationIssueNumber"/>
|
||||
<ref bean="sortTitle"/>
|
||||
<ref bean="sortCreativeWorkDatePublished"/>
|
||||
@@ -1229,11 +1205,9 @@
|
||||
<!--The sort filters for the discovery search-->
|
||||
<property name="searchSortConfiguration">
|
||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
||||
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
|
||||
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
<property name="sortFields">
|
||||
<list>
|
||||
<ref bean="sortScore" />
|
||||
<ref bean="sortPublicationVolumeNumber"/>
|
||||
<ref bean="sortTitle"/>
|
||||
<ref bean="sortCreativeWorkDatePublished"/>
|
||||
@@ -1290,11 +1264,9 @@
|
||||
<!--The sort filters for the discovery search-->
|
||||
<property name="searchSortConfiguration">
|
||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
||||
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
|
||||
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
<property name="sortFields">
|
||||
<list>
|
||||
<ref bean="sortScore" />
|
||||
<ref bean="sortTitle"/>
|
||||
<ref bean="sortCreativeWorkDatePublished"/>
|
||||
<ref bean="sortDateAccessioned"/>
|
||||
@@ -1360,9 +1332,6 @@
|
||||
<!--The sort filters for the discovery search-->
|
||||
<property name="searchSortConfiguration">
|
||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
||||
<property name="defaultSort" ref="sortEntityType"/>
|
||||
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
<property name="sortFields">
|
||||
<list>
|
||||
<ref bean="sortEntityType"/>
|
||||
@@ -1374,7 +1343,6 @@
|
||||
<ref bean="sortFamilyName"/>
|
||||
<ref bean="sortGivenName"/>
|
||||
<ref bean="sortBirthDate"/>
|
||||
<ref bean="sortDateAccessioned"/>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
@@ -1422,11 +1390,9 @@
|
||||
<!--The sort filters for the discovery search-->
|
||||
<property name="searchSortConfiguration">
|
||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
||||
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
|
||||
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
<property name="sortFields">
|
||||
<list>
|
||||
<ref bean="sortScore" />
|
||||
<ref bean="sortOrganizationLegalName"/>
|
||||
<ref bean="sortOrganizationAddressCountry"/>
|
||||
<ref bean="sortOrganizationAddressLocality"/>
|
||||
@@ -2180,59 +2146,77 @@
|
||||
</bean>
|
||||
|
||||
<!--Sort properties-->
|
||||
<bean id="sortScore" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
</bean>
|
||||
|
||||
<bean id="sortTitle" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||
<property name="metadataField" value="dc.title"/>
|
||||
<property name="defaultSortOrder" value="asc"/>
|
||||
</bean>
|
||||
|
||||
<bean id="sortDateIssued" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||
<property name="metadataField" value="dc.date.issued"/>
|
||||
<property name="type" value="date"/>
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
</bean>
|
||||
<bean id="sortDateAccessioned" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||
<property name="metadataField" value="dc.date.accessioned"/>
|
||||
<property name="type" value="date"/>
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
</bean>
|
||||
|
||||
<bean id="sortFamilyName" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||
<property name="metadataField" value="person.familyName"/>
|
||||
<property name="defaultSortOrder" value="asc"/>
|
||||
</bean>
|
||||
<bean id="sortGivenName" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||
<property name="metadataField" value="person.givenName"/>
|
||||
<property name="defaultSortOrder" value="asc"/>
|
||||
</bean>
|
||||
<bean id="sortBirthDate" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||
<property name="metadataField" value="person.birthDate"/>
|
||||
<property name="type" value="date"/>
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
</bean>
|
||||
|
||||
<bean id="sortOrganizationLegalName" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||
<property name="metadataField" value="organization.legalName"/>
|
||||
<property name="defaultSortOrder" value="asc"/>
|
||||
</bean>
|
||||
<bean id="sortOrganizationAddressCountry"
|
||||
class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||
<property name="metadataField" value="organisation.address.addressCountry"/>
|
||||
<property name="defaultSortOrder" value="asc"/>
|
||||
</bean>
|
||||
<bean id="sortOrganizationAddressLocality"
|
||||
class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||
<property name="metadataField" value="organisation.address.addressLocality"/>
|
||||
<property name="defaultSortOrder" value="asc"/>
|
||||
</bean>
|
||||
<bean id="sortOrganizationFoundingDate" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||
<property name="metadataField" value="organisation.foundingDate"/>
|
||||
<property name="type" value="date"/>
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
</bean>
|
||||
|
||||
<bean id="sortPublicationIssueNumber" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||
<property name="metadataField" value="publicationissue.issueNumber"/>
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
</bean>
|
||||
<bean id="sortCreativeWorkDatePublished" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||
<property name="metadataField" value="creativework.datePublished"/>
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
</bean>
|
||||
|
||||
<bean id="sortPublicationVolumeNumber" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||
<property name="metadataField" value="publicationvolume.volumeNumber"/>
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
</bean>
|
||||
|
||||
<bean id="sortEntityType" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||
<property name="metadataField" value="dspace.entity.type"/>
|
||||
<property name="defaultSortOrder" value="desc"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
@@ -21,6 +21,7 @@
|
||||
<!-- serves as a 'this field is required' flag. -->
|
||||
|
||||
<form-definitions>
|
||||
<!-- Form used for entering in Bitstream/File metadata after uploading a file -->
|
||||
<form name="bitstream-metadata">
|
||||
<row>
|
||||
<field>
|
||||
@@ -47,7 +48,203 @@
|
||||
</row>
|
||||
</form>
|
||||
|
||||
<!-- Form which defines the first page/section of the "traditional" DSpace submission process,
|
||||
often used when depositing normal Items (i.e. Items which are not Entities). -->
|
||||
<form name="traditionalpageone">
|
||||
<row>
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>contributor</dc-element>
|
||||
<dc-qualifier>author</dc-qualifier>
|
||||
<repeatable>true</repeatable>
|
||||
<label>Author</label>
|
||||
<input-type>onebox</input-type>
|
||||
<hint>Enter the author's name (Family name, Given names).</hint>
|
||||
<required></required>
|
||||
</field>
|
||||
</row>
|
||||
<row>
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>title</dc-element>
|
||||
<dc-qualifier></dc-qualifier>
|
||||
<repeatable>false</repeatable>
|
||||
<label>Title</label>
|
||||
<input-type>onebox</input-type>
|
||||
<hint>Enter the main title of the item.</hint>
|
||||
<required>You must enter a main title for this item.</required>
|
||||
</field>
|
||||
</row>
|
||||
<row>
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>title</dc-element>
|
||||
<dc-qualifier>alternative</dc-qualifier>
|
||||
<repeatable>true</repeatable>
|
||||
<label>Other Titles</label>
|
||||
<input-type>onebox</input-type>
|
||||
<hint>If the item has any alternative titles, please enter them here.</hint>
|
||||
<required></required>
|
||||
</field>
|
||||
</row>
|
||||
<row>
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>date</dc-element>
|
||||
<dc-qualifier>issued</dc-qualifier>
|
||||
<repeatable>false</repeatable>
|
||||
<label>Date of Issue</label>
|
||||
<style>col-sm-4</style>
|
||||
<input-type>date</input-type>
|
||||
<hint>Please give the date of previous publication or public distribution.
|
||||
You can leave out the day and/or month if they aren't applicable.
|
||||
</hint>
|
||||
<required>You must enter at least the year.</required>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>publisher</dc-element>
|
||||
<dc-qualifier></dc-qualifier>
|
||||
<repeatable>false</repeatable>
|
||||
<label>Publisher</label>
|
||||
<style>col-sm-8</style>
|
||||
<input-type>onebox</input-type>
|
||||
<hint>Enter the name of the publisher of the previously issued instance of this item.</hint>
|
||||
<required></required>
|
||||
</field>
|
||||
</row>
|
||||
<row>
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>identifier</dc-element>
|
||||
<dc-qualifier>citation</dc-qualifier>
|
||||
<repeatable>false</repeatable>
|
||||
<label>Citation</label>
|
||||
<input-type>onebox</input-type>
|
||||
<hint>Enter the standard citation for the previously issued instance of this item.</hint>
|
||||
<required></required>
|
||||
</field>
|
||||
</row>
|
||||
<row>
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>relation</dc-element>
|
||||
<dc-qualifier>ispartofseries</dc-qualifier>
|
||||
<repeatable>true</repeatable>
|
||||
<label>Series/Report No.</label>
|
||||
<input-type>series</input-type>
|
||||
<hint>Enter the series and number assigned to this item by your community.</hint>
|
||||
<required></required>
|
||||
</field>
|
||||
</row>
|
||||
<row>
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>identifier</dc-element>
|
||||
<dc-qualifier></dc-qualifier>
|
||||
<!-- An input-type of qualdrop_value MUST be marked as repeatable -->
|
||||
<repeatable>true</repeatable>
|
||||
<label>Identifiers</label>
|
||||
<input-type value-pairs-name="common_identifiers">qualdrop_value</input-type>
|
||||
<hint>If the item has any identification numbers or codes associated with
|
||||
it, please enter the types and the actual numbers or codes.
|
||||
</hint>
|
||||
<required></required>
|
||||
</field>
|
||||
</row>
|
||||
<row>
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>type</dc-element>
|
||||
<dc-qualifier></dc-qualifier>
|
||||
<repeatable>true</repeatable>
|
||||
<label>Type</label>
|
||||
<input-type value-pairs-name="common_types">dropdown</input-type>
|
||||
<hint>Select the type(s) of content of the item. To select more than one value in the list, you may
|
||||
have to hold down the "CTRL" or "Shift" key.
|
||||
</hint>
|
||||
<required></required>
|
||||
</field>
|
||||
</row>
|
||||
<row>
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>language</dc-element>
|
||||
<dc-qualifier>iso</dc-qualifier>
|
||||
<repeatable>false</repeatable>
|
||||
<label>Language</label>
|
||||
<input-type value-pairs-name="common_iso_languages">dropdown</input-type>
|
||||
<hint>Select the language of the main content of the item. If the language does not appear in the
|
||||
list, please select 'Other'. If the content does not really have a language (for example, if it
|
||||
is a dataset or an image) please select 'N/A'.
|
||||
</hint>
|
||||
<required></required>
|
||||
</field>
|
||||
</row>
|
||||
</form>
|
||||
|
||||
<!-- Form which defines the second page/section of the "traditional" DSpace submission process,
|
||||
often used when depositing normal Items. -->
|
||||
<form name="traditionalpagetwo">
|
||||
<row>
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>subject</dc-element>
|
||||
<dc-qualifier></dc-qualifier>
|
||||
<!-- An input-type of tag MUST be marked as repeatable -->
|
||||
<repeatable>true</repeatable>
|
||||
<label>Subject Keywords</label>
|
||||
<input-type>tag</input-type>
|
||||
<hint>Enter appropriate subject keywords or phrases.</hint>
|
||||
<required></required>
|
||||
<vocabulary>srsc</vocabulary>
|
||||
</field>
|
||||
</row>
|
||||
<row>
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>description</dc-element>
|
||||
<dc-qualifier>abstract</dc-qualifier>
|
||||
<repeatable>false</repeatable>
|
||||
<label>Abstract</label>
|
||||
<input-type>textarea</input-type>
|
||||
<hint>Enter the abstract of the item.</hint>
|
||||
<required></required>
|
||||
</field>
|
||||
</row>
|
||||
<row>
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>description</dc-element>
|
||||
<dc-qualifier>sponsorship</dc-qualifier>
|
||||
<repeatable>false</repeatable>
|
||||
<label>Sponsors</label>
|
||||
<input-type>textarea</input-type>
|
||||
<hint>Enter the names of any sponsors and/or funding codes in the box.</hint>
|
||||
<required></required>
|
||||
</field>
|
||||
</row>
|
||||
<row>
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>description</dc-element>
|
||||
<dc-qualifier></dc-qualifier>
|
||||
<repeatable>false</repeatable>
|
||||
<label>Description</label>
|
||||
<input-type>textarea</input-type>
|
||||
<hint>Enter any other description or comments in this box.</hint>
|
||||
<required></required>
|
||||
</field>
|
||||
</row>
|
||||
</form>
|
||||
|
||||
|
||||
<!-- Form which defines the *first* metadata page/section for depositing Publication Entities.
|
||||
Since Publication Entities are very similar to normal Items, this is nearly identical to the
|
||||
'traditionalpageone' form. The only difference is the Author field can be used to link the
|
||||
Publication Entity to a Person Entity. -->
|
||||
<form name="publicationStep">
|
||||
<row>
|
||||
<relation-field>
|
||||
<relationship-type>isAuthorOfPublication</relationship-type>
|
||||
@@ -190,60 +387,8 @@
|
||||
</row>
|
||||
</form>
|
||||
|
||||
<form name="traditionalpagetwo">
|
||||
<row>
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>subject</dc-element>
|
||||
<dc-qualifier></dc-qualifier>
|
||||
<!-- An input-type of tag MUST be marked as repeatable -->
|
||||
<repeatable>true</repeatable>
|
||||
<label>Subject Keywords</label>
|
||||
<input-type>tag</input-type>
|
||||
<hint>Enter appropriate subject keywords or phrases.</hint>
|
||||
<required></required>
|
||||
<vocabulary>srsc</vocabulary>
|
||||
</field>
|
||||
</row>
|
||||
<row>
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>description</dc-element>
|
||||
<dc-qualifier>abstract</dc-qualifier>
|
||||
<repeatable>false</repeatable>
|
||||
<label>Abstract</label>
|
||||
<input-type>textarea</input-type>
|
||||
<hint>Enter the abstract of the item.</hint>
|
||||
<required></required>
|
||||
</field>
|
||||
</row>
|
||||
<row>
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>description</dc-element>
|
||||
<dc-qualifier>sponsorship</dc-qualifier>
|
||||
<repeatable>false</repeatable>
|
||||
<label>Sponsors</label>
|
||||
<input-type>textarea</input-type>
|
||||
<hint>Enter the names of any sponsors and/or funding codes in the box.</hint>
|
||||
<required></required>
|
||||
</field>
|
||||
</row>
|
||||
<row>
|
||||
<field>
|
||||
<dc-schema>dc</dc-schema>
|
||||
<dc-element>description</dc-element>
|
||||
<dc-qualifier></dc-qualifier>
|
||||
<repeatable>false</repeatable>
|
||||
<label>Description</label>
|
||||
<input-type>textarea</input-type>
|
||||
<hint>Enter any other description or comments in this box.</hint>
|
||||
<required></required>
|
||||
</field>
|
||||
</row>
|
||||
</form>
|
||||
|
||||
<form name="peopleStep">
|
||||
<!-- Form which defines the metadata page/section for depositing Person Entities -->
|
||||
<form name="personStep">
|
||||
<row>
|
||||
<relation-field>
|
||||
<relationship-type>isPublicationOfAuthor</relationship-type>
|
||||
@@ -320,6 +465,7 @@
|
||||
</row>
|
||||
</form>
|
||||
|
||||
<!-- Form which defines the metadata page/section for depositing Project Entities -->
|
||||
<form name="projectStep">
|
||||
<row>
|
||||
<field>
|
||||
@@ -390,6 +536,7 @@
|
||||
</row>
|
||||
</form>
|
||||
|
||||
<!-- Form which defines the metadata page/section for depositing OrgUnit Entities -->
|
||||
<form name="orgUnitStep">
|
||||
<row>
|
||||
<field>
|
||||
@@ -449,6 +596,7 @@
|
||||
</row>
|
||||
</form>
|
||||
|
||||
<!-- Form which defines the metadata page/section for depositing Journal Entities -->
|
||||
<form name="journalStep">
|
||||
<row>
|
||||
<field>
|
||||
@@ -498,6 +646,7 @@
|
||||
|
||||
</form>
|
||||
|
||||
<!-- Form which defines the metadata page/section for depositing JournalVolume Entities -->
|
||||
<form name="journalVolumeStep">
|
||||
<row>
|
||||
<relation-field>
|
||||
@@ -548,6 +697,7 @@
|
||||
</row>
|
||||
</form>
|
||||
|
||||
<!-- Form which defines the metadata page/section for depositing JournalIssue Entities -->
|
||||
<form name="journalIssueStep">
|
||||
<row>
|
||||
<relation-field>
|
||||
|
@@ -17,7 +17,7 @@
|
||||
<parent>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>modules</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@@ -11,7 +11,7 @@
|
||||
<parent>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-parent</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@@ -13,7 +13,7 @@
|
||||
<parent>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>modules</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@@ -13,7 +13,7 @@ just adding new jar in the classloader</description>
|
||||
<parent>
|
||||
<artifactId>modules</artifactId>
|
||||
<groupId>org.dspace</groupId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@@ -16,7 +16,7 @@
|
||||
<parent>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-parent</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@@ -11,7 +11,8 @@ version: "3.7"
|
||||
services:
|
||||
dspace-cli:
|
||||
environment:
|
||||
- LOADASSETS=https://www.dropbox.com/s/v3ahfcuatklbmi0/assetstore-2019-11-28.tar.gz?dl=1
|
||||
# This assetstore zip is available from https://github.com/DSpace-Labs/AIP-Files/releases/tag/demo-entities-data
|
||||
- LOADASSETS=https://github.com/DSpace-Labs/AIP-Files/releases/download/demo-entities-data/assetstore.tar.gz
|
||||
entrypoint:
|
||||
- /bin/bash
|
||||
- '-c'
|
||||
|
@@ -12,5 +12,33 @@ services:
|
||||
dspacedb:
|
||||
image: dspace/dspace-postgres-pgcrypto:loadsql
|
||||
environment:
|
||||
# Double underbars in env names will be replaced with periods for apache commons
|
||||
- LOADSQL=https://www.dropbox.com/s/4ap1y6deseoc8ws/dspace7-entities-2019-11-28.sql?dl=1
|
||||
# This SQL is available from https://github.com/DSpace-Labs/AIP-Files/releases/tag/demo-entities-data
|
||||
- LOADSQL=https://github.com/DSpace-Labs/AIP-Files/releases/download/demo-entities-data/dspace7-entities-2021-04-14.sql
|
||||
dspace:
|
||||
### OVERRIDE default 'entrypoint' in 'docker-compose.yml ####
|
||||
# Ensure that the database is ready BEFORE starting tomcat
|
||||
# 1. While a TCP connection to dspacedb port 5432 is not available, continue to sleep
|
||||
# 2. Then, run database migration to init database tables
|
||||
# 3. (Custom for Entities) enable Entity-specific collection submission mappings in item-submission.xml
|
||||
# This 'sed' command inserts the sample configurations specific to the Entities data set, see:
|
||||
# https://github.com/DSpace/DSpace/blob/main/dspace/config/item-submission.xml#L36-L49
|
||||
# 4. Finally, start Tomcat
|
||||
entrypoint:
|
||||
- /bin/bash
|
||||
- '-c'
|
||||
- |
|
||||
while (!</dev/tcp/dspacedb/5432) > /dev/null 2>&1; do sleep 1; done;
|
||||
/dspace/bin/dspace database migrate
|
||||
sed -i '/name-map collection-handle="default".*/a \\n <name-map collection-handle="123456789/3" submission-name="Publication"/> \
|
||||
<name-map collection-handle="123456789/4" submission-name="Publication"/> \
|
||||
<name-map collection-handle="123456789/281" submission-name="Publication"/> \
|
||||
<name-map collection-handle="123456789/5" submission-name="Publication"/> \
|
||||
<name-map collection-handle="123456789/8" submission-name="OrgUnit"/> \
|
||||
<name-map collection-handle="123456789/6" submission-name="Person"/> \
|
||||
<name-map collection-handle="123456789/279" submission-name="Person"/> \
|
||||
<name-map collection-handle="123456789/7" submission-name="Project"/> \
|
||||
<name-map collection-handle="123456789/280" submission-name="Project"/> \
|
||||
<name-map collection-handle="123456789/28" submission-name="Journal"/> \
|
||||
<name-map collection-handle="123456789/29" submission-name="JournalVolume"/> \
|
||||
<name-map collection-handle="123456789/30" submission-name="JournalIssue"/>' /dspace/config/item-submission.xml
|
||||
catalina.sh run
|
||||
|
@@ -1,6 +1,11 @@
|
||||
dspace.dir=/dspace
|
||||
db.url=jdbc:postgresql://dspacedb:5432/dspace
|
||||
dspace.server.url=http://localhost:8080/server
|
||||
dspace.ui.url=http://localhost:4000
|
||||
dspace.name=DSpace Started with Docker Compose
|
||||
# Ensure we are using the 'dspacedb' image for our database
|
||||
db.url=jdbc:postgresql://dspacedb:5432/dspace
|
||||
# Ensure we are using the 'dspacesolr' image for Solr
|
||||
solr.server=http://dspacesolr:8983/solr
|
||||
# NOTE: This setting is required for a REST API running in Docker to trust requests from the host machine.
|
||||
# This IP range MUST correspond to the 'dspacenet' subnet defined in our 'docker-compose.yml'.
|
||||
proxies.trusted.ipranges = 172.23.0
|
||||
|
34
pom.xml
34
pom.xml
@@ -4,7 +4,7 @@
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<name>DSpace Parent Project</name>
|
||||
<description>
|
||||
DSpace open source software is a turnkey institutional repository application.
|
||||
@@ -30,12 +30,12 @@
|
||||
<axiom.version>1.2.22</axiom.version>
|
||||
<errorprone.version>2.3.4</errorprone.version>
|
||||
<!-- NOTE: when updating jackson.version, also sync jackson-databind dependency below -->
|
||||
<jackson.version>2.10.2</jackson.version>
|
||||
<jackson.version>2.12.3</jackson.version>
|
||||
<javax-annotation.version>1.3.2</javax-annotation.version>
|
||||
<jaxb-api.version>2.3.1</jaxb-api.version>
|
||||
<jaxb-runtime.version>2.3.1</jaxb-runtime.version>
|
||||
<!-- NOTE: Jetty needed for Solr, Handle Server & tests -->
|
||||
<jetty.version>9.4.35.v20201120</jetty.version>
|
||||
<jetty.version>9.4.38.v20210224</jetty.version>
|
||||
<log4j.version>2.13.3</log4j.version>
|
||||
<pdfbox-version>2.0.15</pdfbox-version>
|
||||
<poi-version>3.17</poi-version>
|
||||
@@ -826,14 +826,14 @@
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-rest</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<type>jar</type>
|
||||
<classifier>classes</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-rest</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<type>war</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
@@ -980,64 +980,64 @@
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-api</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-api</artifactId>
|
||||
<type>test-jar</type>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dspace.modules</groupId>
|
||||
<artifactId>additions</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-sword</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-swordv2</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-oai</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-services</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-server-webapp</artifactId>
|
||||
<type>test-jar</type>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-rdf</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-server-webapp</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<type>jar</type>
|
||||
<classifier>classes</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-server-webapp</artifactId>
|
||||
<version>7.0-beta5-SNAPSHOT</version>
|
||||
<version>7.0-beta6-SNAPSHOT</version>
|
||||
<type>war</type>
|
||||
</dependency>
|
||||
<!-- DSpace API Localization Packages -->
|
||||
@@ -1369,7 +1369,7 @@
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.6</version>
|
||||
<version>2.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
|
Reference in New Issue
Block a user