mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 10:04:21 +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/).
|
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:
|
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)
|
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.
|
and a servlet container (usually Tomcat) in order to function.
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
version: '3.7'
|
version: '3.7'
|
||||||
networks:
|
networks:
|
||||||
dspacenet:
|
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:
|
services:
|
||||||
# DSpace (backend) webapp container
|
# DSpace (backend) webapp container
|
||||||
dspace:
|
dspace:
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-parent</artifactId>
|
<artifactId>dspace-parent</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<relativePath>..</relativePath>
|
<relativePath>..</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
@@ -856,6 +856,10 @@
|
|||||||
<groupId>org.javassist</groupId>
|
<groupId>org.javassist</groupId>
|
||||||
<artifactId>javassist</artifactId>
|
<artifactId>javassist</artifactId>
|
||||||
</exclusion>
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>io.swagger</groupId>
|
||||||
|
<artifactId>swagger-jersey-jaxrs</artifactId>
|
||||||
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
@@ -8,9 +8,12 @@
|
|||||||
package org.dspace.app.bulkedit;
|
package org.dspace.app.bulkedit;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.apache.commons.cli.ParseException;
|
import org.apache.commons.cli.ParseException;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
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.DSpaceObject;
|
||||||
import org.dspace.content.factory.ContentServiceFactory;
|
import org.dspace.content.factory.ContentServiceFactory;
|
||||||
import org.dspace.content.service.MetadataDSpaceCsvExportService;
|
import org.dspace.content.service.MetadataDSpaceCsvExportService;
|
||||||
@@ -30,7 +33,7 @@ public class MetadataExport extends DSpaceRunnable<MetadataExportScriptConfigura
|
|||||||
|
|
||||||
private boolean help = false;
|
private boolean help = false;
|
||||||
private String filename = null;
|
private String filename = null;
|
||||||
private String handle = null;
|
private String identifier = null;
|
||||||
private boolean exportAllMetadata = false;
|
private boolean exportAllMetadata = false;
|
||||||
private boolean exportAllItems = false;
|
private boolean exportAllItems = false;
|
||||||
|
|
||||||
@@ -41,6 +44,8 @@ public class MetadataExport extends DSpaceRunnable<MetadataExportScriptConfigura
|
|||||||
|
|
||||||
private EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService();
|
private EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService();
|
||||||
|
|
||||||
|
private DSpaceObjectUtils dSpaceObjectUtils = UtilServiceFactory.getInstance().getDSpaceObjectUtils();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void internalRun() throws Exception {
|
public void internalRun() throws Exception {
|
||||||
|
|
||||||
@@ -57,7 +62,7 @@ public class MetadataExport extends DSpaceRunnable<MetadataExportScriptConfigura
|
|||||||
handler.handleException(e);
|
handler.handleException(e);
|
||||||
}
|
}
|
||||||
DSpaceCSV dSpaceCSV = metadataDSpaceCsvExportService
|
DSpaceCSV dSpaceCSV = metadataDSpaceCsvExportService
|
||||||
.handleExport(context, exportAllItems, exportAllMetadata, handle,
|
.handleExport(context, exportAllItems, exportAllMetadata, identifier,
|
||||||
handler);
|
handler);
|
||||||
handler.writeFilestream(context, filename, dSpaceCSV.getInputStream(), EXPORT_CSV);
|
handler.writeFilestream(context, filename, dSpaceCSV.getInputStream(), EXPORT_CSV);
|
||||||
context.restoreAuthSystemState();
|
context.restoreAuthSystemState();
|
||||||
@@ -66,7 +71,7 @@ public class MetadataExport extends DSpaceRunnable<MetadataExportScriptConfigura
|
|||||||
|
|
||||||
protected void logHelpInfo() {
|
protected void logHelpInfo() {
|
||||||
handler.logInfo("\nfull export: metadata-export");
|
handler.logInfo("\nfull export: metadata-export");
|
||||||
handler.logInfo("partial export: metadata-export -i handle");
|
handler.logInfo("partial export: metadata-export -i handle/UUID");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -86,7 +91,7 @@ public class MetadataExport extends DSpaceRunnable<MetadataExportScriptConfigura
|
|||||||
if (!commandLine.hasOption('i')) {
|
if (!commandLine.hasOption('i')) {
|
||||||
exportAllItems = true;
|
exportAllItems = true;
|
||||||
}
|
}
|
||||||
handle = commandLine.getOptionValue('i');
|
identifier = commandLine.getOptionValue('i');
|
||||||
filename = getFileNameForExportFile();
|
filename = getFileNameForExportFile();
|
||||||
|
|
||||||
exportAllMetadata = commandLine.hasOption('a');
|
exportAllMetadata = commandLine.hasOption('a');
|
||||||
@@ -97,17 +102,20 @@ public class MetadataExport extends DSpaceRunnable<MetadataExportScriptConfigura
|
|||||||
Context context = new Context();
|
Context context = new Context();
|
||||||
try {
|
try {
|
||||||
DSpaceObject dso = null;
|
DSpaceObject dso = null;
|
||||||
if (StringUtils.isNotBlank(handle)) {
|
if (StringUtils.isNotBlank(identifier)) {
|
||||||
dso = HandleServiceFactory.getInstance().getHandleService().resolveToObject(context, handle);
|
dso = HandleServiceFactory.getInstance().getHandleService().resolveToObject(context, identifier);
|
||||||
|
if (dso == null) {
|
||||||
|
dso = dSpaceObjectUtils.findDSpaceObject(context, UUID.fromString(identifier));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
dso = ContentServiceFactory.getInstance().getSiteService().findSite(context);
|
dso = ContentServiceFactory.getInstance().getSiteService().findSite(context);
|
||||||
}
|
}
|
||||||
if (dso == null) {
|
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";
|
return dso.getID().toString() + ".csv";
|
||||||
} catch (SQLException e) {
|
} 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;
|
return null;
|
||||||
}
|
}
|
||||||
|
@@ -152,8 +152,7 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
|
|||||||
@Autowired(required = true)
|
@Autowired(required = true)
|
||||||
protected ConfigurationService configurationService;
|
protected ConfigurationService configurationService;
|
||||||
|
|
||||||
protected final String tempWorkDir
|
protected String tempWorkDir;
|
||||||
= configurationService.getProperty("org.dspace.app.batchitemimport.work.dir");
|
|
||||||
|
|
||||||
protected boolean isTest = false;
|
protected boolean isTest = false;
|
||||||
protected boolean isResume = false;
|
protected boolean isResume = false;
|
||||||
@@ -163,6 +162,7 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
tempWorkDir = configurationService.getProperty("org.dspace.app.batchitemimport.work.dir");
|
||||||
//Ensure tempWorkDir exists
|
//Ensure tempWorkDir exists
|
||||||
File tempWorkDirFile = new File(tempWorkDir);
|
File tempWorkDirFile = new File(tempWorkDir);
|
||||||
if (!tempWorkDirFile.exists()) {
|
if (!tempWorkDirFile.exists()) {
|
||||||
|
@@ -10,12 +10,16 @@ package org.dspace.app.launcher;
|
|||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import org.apache.commons.cli.ParseException;
|
import org.apache.commons.cli.ParseException;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.dspace.core.Context;
|
||||||
import org.dspace.scripts.DSpaceRunnable;
|
import org.dspace.scripts.DSpaceRunnable;
|
||||||
import org.dspace.scripts.configuration.ScriptConfiguration;
|
import org.dspace.scripts.configuration.ScriptConfiguration;
|
||||||
import org.dspace.scripts.factory.ScriptServiceFactory;
|
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
|
* @param commandConfigs configs as Document
|
||||||
*/
|
*/
|
||||||
private static void display(Document commandConfigs) {
|
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 all command elements
|
||||||
List<Element> commands = commandConfigs.getRootElement().getChildren("command");
|
List<Element> commands = commandConfigs.getRootElement().getChildren("command");
|
||||||
|
|
||||||
@@ -334,11 +380,32 @@ public class ScriptLauncher {
|
|||||||
sortedCommands.put(command.getChild("name").getValue(), command);
|
sortedCommands.put(command.getChild("name").getValue(), command);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display the sorted list
|
return sortedCommands.values();
|
||||||
System.out.println("Usage: dspace [command-name] {parameters}");
|
|
||||||
for (Element command : sortedCommands.values()) {
|
|
||||||
System.out.println(" - " + command.getChild("name").getValue() +
|
|
||||||
": " + command.getChild("description").getValue());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
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.MetadataExposureService;
|
||||||
import org.dspace.app.util.service.OpenSearchService;
|
import org.dspace.app.util.service.OpenSearchService;
|
||||||
import org.dspace.app.util.service.WebAppService;
|
import org.dspace.app.util.service.WebAppService;
|
||||||
@@ -25,6 +26,8 @@ public abstract class UtilServiceFactory {
|
|||||||
|
|
||||||
public abstract MetadataExposureService getMetadataExposureService();
|
public abstract MetadataExposureService getMetadataExposureService();
|
||||||
|
|
||||||
|
public abstract DSpaceObjectUtils getDSpaceObjectUtils();
|
||||||
|
|
||||||
public static UtilServiceFactory getInstance() {
|
public static UtilServiceFactory getInstance() {
|
||||||
return DSpaceServicesFactory.getInstance().getServiceManager()
|
return DSpaceServicesFactory.getInstance().getServiceManager()
|
||||||
.getServiceByName("appUtilServiceFactory", UtilServiceFactory.class);
|
.getServiceByName("appUtilServiceFactory", UtilServiceFactory.class);
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.util.factory;
|
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.MetadataExposureService;
|
||||||
import org.dspace.app.util.service.OpenSearchService;
|
import org.dspace.app.util.service.OpenSearchService;
|
||||||
import org.dspace.app.util.service.WebAppService;
|
import org.dspace.app.util.service.WebAppService;
|
||||||
@@ -26,6 +27,8 @@ public class UtilServiceFactoryImpl extends UtilServiceFactory {
|
|||||||
private OpenSearchService openSearchService;
|
private OpenSearchService openSearchService;
|
||||||
@Autowired(required = true)
|
@Autowired(required = true)
|
||||||
private WebAppService webAppService;
|
private WebAppService webAppService;
|
||||||
|
@Autowired(required = true)
|
||||||
|
private DSpaceObjectUtils dSpaceObjectUtils;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WebAppService getWebAppService() {
|
public WebAppService getWebAppService() {
|
||||||
@@ -41,4 +44,9 @@ public class UtilServiceFactoryImpl extends UtilServiceFactory {
|
|||||||
public MetadataExposureService getMetadataExposureService() {
|
public MetadataExposureService getMetadataExposureService() {
|
||||||
return metadataExposureService;
|
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.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
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
|
* 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
|
* redirect to obtain credentials (either password prompt or e.g. HTTPS port
|
||||||
* for client cert.); null means no redirect.
|
* 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 context DSpace context, will be modified (ePerson set) upon success.
|
||||||
* @param request The HTTP request that started this operation, or null if not
|
* @param request The HTTP request that started this operation, or null if not
|
||||||
* applicable.
|
* applicable.
|
||||||
@@ -507,8 +515,8 @@ public class ShibAuthentication implements AuthenticationMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Determine the server return URL, where shib will send the user after authenticating.
|
// 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
|
// We need it to go back to DSpace's ShibbolethRestController so we will extract the user's information,
|
||||||
// and locally authenticate them.
|
// locally authenticate them & then redirect back to the UI.
|
||||||
String returnURL = configurationService.getProperty("dspace.server.url") + "/api/authn/shibboleth"
|
String returnURL = configurationService.getProperty("dspace.server.url") + "/api/authn/shibboleth"
|
||||||
+ ((redirectUrl != null) ? "?redirectUrl=" + redirectUrl : "");
|
+ ((redirectUrl != null) ? "?redirectUrl=" + redirectUrl : "");
|
||||||
|
|
||||||
@@ -533,6 +541,25 @@ public class ShibAuthentication implements AuthenticationMethod {
|
|||||||
return "shibboleth";
|
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
|
* Identify an existing EPerson based upon the shibboleth attributes provided on
|
||||||
* the request object. There are three cases where this can occurr, each as
|
* 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.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
import org.apache.commons.lang.NotImplementedException;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.logging.log4j.Logger;
|
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,
|
public List<MetadataValue> addMetadata(Context context, T dso, MetadataField metadataField, String lang,
|
||||||
List<String> values, List<String> authorities, List<Integer> confidences)
|
List<String> values, List<String> authorities, List<Integer> confidences)
|
||||||
throws SQLException {
|
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 authorityControlled = metadataAuthorityService.isAuthorityControlled(metadataField);
|
||||||
boolean authorityRequired = metadataAuthorityService.isAuthorityRequired(metadataField);
|
boolean authorityRequired = metadataAuthorityService.isAuthorityRequired(metadataField);
|
||||||
List<MetadataValue> newMetadata = new ArrayList<>(values.size());
|
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);
|
MetadataValue metadataValue = metadataValueService.create(context, dso, metadataField);
|
||||||
newMetadata.add(metadataValue);
|
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(placeSupplier.get());
|
||||||
metadataValue.setPlace(
|
|
||||||
this.getMetadata(dso, metadataField.getMetadataSchema().getName(), metadataField.getElement(),
|
|
||||||
metadataField.getQualifier(), Item.ANY).size() - 1);
|
|
||||||
|
|
||||||
metadataValue.setLanguage(lang == null ? null : lang.trim());
|
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,
|
public MetadataValue addMetadata(Context context, T dso, String schema, String element, String qualifier,
|
||||||
String lang, String value, String authority, int confidence) throws SQLException {
|
String lang, String value, String authority, int confidence) throws SQLException {
|
||||||
return addMetadata(context, dso, schema, element, qualifier, lang, Arrays.asList(value),
|
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
|
@Override
|
||||||
@@ -805,4 +819,12 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
|
|||||||
dso.setMetadataModified();
|
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.io.InputStream;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@@ -1405,5 +1407,25 @@ prevent the generation of resource policy entry values with null dspace_object a
|
|||||||
return listToReturn;
|
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.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import com.google.common.collect.Iterators;
|
import com.google.common.collect.Iterators;
|
||||||
import org.dspace.app.bulkedit.DSpaceCSV;
|
import org.dspace.app.bulkedit.DSpaceCSV;
|
||||||
|
import org.dspace.app.util.service.DSpaceObjectUtils;
|
||||||
import org.dspace.content.service.ItemService;
|
import org.dspace.content.service.ItemService;
|
||||||
import org.dspace.content.service.MetadataDSpaceCsvExportService;
|
import org.dspace.content.service.MetadataDSpaceCsvExportService;
|
||||||
import org.dspace.core.Constants;
|
import org.dspace.core.Constants;
|
||||||
@@ -31,8 +33,11 @@ public class MetadataDSpaceCsvExportServiceImpl implements MetadataDSpaceCsvExpo
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ItemService itemService;
|
private ItemService itemService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DSpaceObjectUtils dSpaceObjectUtils;
|
||||||
|
|
||||||
@Override
|
@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 {
|
DSpaceRunnableHandler handler) throws Exception {
|
||||||
Iterator<Item> toExport = null;
|
Iterator<Item> toExport = null;
|
||||||
|
|
||||||
@@ -40,26 +45,32 @@ public class MetadataDSpaceCsvExportServiceImpl implements MetadataDSpaceCsvExpo
|
|||||||
handler.logInfo("Exporting whole repository WARNING: May take some time!");
|
handler.logInfo("Exporting whole repository WARNING: May take some time!");
|
||||||
toExport = itemService.findAll(context);
|
toExport = itemService.findAll(context);
|
||||||
} else {
|
} 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) {
|
if (dso == null) {
|
||||||
throw new IllegalArgumentException(
|
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) {
|
if (dso.getType() == Constants.ITEM) {
|
||||||
handler.logInfo("Exporting item '" + dso.getName() + "' (" + handle + ")");
|
handler.logInfo("Exporting item '" + dso.getName() + "' (" + identifier + ")");
|
||||||
List<Item> item = new ArrayList<>();
|
List<Item> item = new ArrayList<>();
|
||||||
item.add((Item) dso);
|
item.add((Item) dso);
|
||||||
toExport = item.iterator();
|
toExport = item.iterator();
|
||||||
} else if (dso.getType() == Constants.COLLECTION) {
|
} else if (dso.getType() == Constants.COLLECTION) {
|
||||||
handler.logInfo("Exporting collection '" + dso.getName() + "' (" + handle + ")");
|
handler.logInfo("Exporting collection '" + dso.getName() + "' (" + identifier + ")");
|
||||||
Collection collection = (Collection) dso;
|
Collection collection = (Collection) dso;
|
||||||
toExport = itemService.findByCollection(context, collection);
|
toExport = itemService.findByCollection(context, collection);
|
||||||
} else if (dso.getType() == Constants.COMMUNITY) {
|
} 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);
|
toExport = buildFromCommunity(context, (Community) dso);
|
||||||
} else {
|
} 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,
|
public MetadataValue addMetadata(Context context, T dso, String schema, String element, String qualifier,
|
||||||
String lang, String value) throws SQLException;
|
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
|
* Add a single metadata field. This is appended to existing
|
||||||
* values. Use <code>clearMetadata</code> to remove values.
|
* 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,
|
public List<MetadataValue> getMetadata(Item item, String schema, String element, String qualifier,
|
||||||
String lang, boolean enableVirtualMetadata);
|
String lang, boolean enableVirtualMetadata);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -28,12 +28,13 @@ public interface MetadataDSpaceCsvExportService {
|
|||||||
* @param context The relevant DSpace context
|
* @param context The relevant DSpace context
|
||||||
* @param exportAllItems A boolean indicating whether or not the entire repository should be exported
|
* @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 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
|
* @return A DSpaceCSV object containing the exported information
|
||||||
* @throws Exception If something goes wrong
|
* @throws Exception If something goes wrong
|
||||||
*/
|
*/
|
||||||
public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean exportAllMetadata,
|
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
|
* 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.content.Item;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.discovery.configuration.DiscoveryConfiguration;
|
||||||
import org.dspace.discovery.configuration.DiscoveryMoreLikeThisConfiguration;
|
import org.dspace.discovery.configuration.DiscoveryMoreLikeThisConfiguration;
|
||||||
import org.dspace.discovery.configuration.DiscoverySearchFilterFacet;
|
import org.dspace.discovery.configuration.DiscoverySearchFilterFacet;
|
||||||
|
|
||||||
@@ -62,11 +63,14 @@ public interface SearchService {
|
|||||||
* @param field the field of the filter query
|
* @param field the field of the filter query
|
||||||
* @param operator equals/notequals/notcontains/authority/notauthority
|
* @param operator equals/notequals/notcontains/authority/notauthority
|
||||||
* @param value the filter query value
|
* @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
|
* @return a filter query
|
||||||
* @throws SQLException if database error
|
* @throws SQLException if database error
|
||||||
* An exception that provides information on a database access error or other errors.
|
* 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,
|
List<Item> getRelatedItems(Context context, Item item,
|
||||||
DiscoveryMoreLikeThisConfiguration moreLikeThisConfiguration);
|
DiscoveryMoreLikeThisConfiguration moreLikeThisConfiguration);
|
||||||
|
@@ -60,6 +60,7 @@ import org.dspace.core.Context;
|
|||||||
import org.dspace.core.Email;
|
import org.dspace.core.Email;
|
||||||
import org.dspace.core.I18nUtil;
|
import org.dspace.core.I18nUtil;
|
||||||
import org.dspace.core.LogManager;
|
import org.dspace.core.LogManager;
|
||||||
|
import org.dspace.discovery.configuration.DiscoveryConfiguration;
|
||||||
import org.dspace.discovery.configuration.DiscoveryConfigurationParameters;
|
import org.dspace.discovery.configuration.DiscoveryConfigurationParameters;
|
||||||
import org.dspace.discovery.configuration.DiscoveryMoreLikeThisConfiguration;
|
import org.dspace.discovery.configuration.DiscoveryMoreLikeThisConfiguration;
|
||||||
import org.dspace.discovery.configuration.DiscoverySearchFilterFacet;
|
import org.dspace.discovery.configuration.DiscoverySearchFilterFacet;
|
||||||
@@ -1069,9 +1070,9 @@ public class SolrServiceImpl implements SearchService, IndexingService {
|
|||||||
return new ArrayList<>(0);
|
return new ArrayList<>(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 {
|
throws SQLException {
|
||||||
DiscoverFilterQuery result = new DiscoverFilterQuery();
|
DiscoverFilterQuery result = new DiscoverFilterQuery();
|
||||||
|
|
||||||
@@ -1081,7 +1082,14 @@ public class SolrServiceImpl implements SearchService, IndexingService {
|
|||||||
|
|
||||||
|
|
||||||
if (operator.endsWith("equals")) {
|
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")) {
|
} else if (operator.endsWith("authority")) {
|
||||||
filterQuery.append("_authority");
|
filterQuery.append("_authority");
|
||||||
}
|
}
|
||||||
|
@@ -20,26 +20,8 @@ public class DiscoverySortConfiguration {
|
|||||||
|
|
||||||
public static final String SCORE = "score";
|
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 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() {
|
public List<DiscoverySortFieldConfiguration> getSortFields() {
|
||||||
return sortFields;
|
return sortFields;
|
||||||
}
|
}
|
||||||
@@ -48,14 +30,6 @@ public class DiscoverySortConfiguration {
|
|||||||
this.sortFields = sortFields;
|
this.sortFields = sortFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SORT_ORDER getDefaultSortOrder() {
|
|
||||||
return defaultSortOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDefaultSortOrder(SORT_ORDER defaultSortOrder) {
|
|
||||||
this.defaultSortOrder = defaultSortOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DiscoverySortFieldConfiguration getSortFieldConfiguration(String sortField) {
|
public DiscoverySortFieldConfiguration getSortFieldConfiguration(String sortField) {
|
||||||
if (StringUtils.isBlank(sortField)) {
|
if (StringUtils.isBlank(sortField)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -67,10 +41,6 @@ public class DiscoverySortConfiguration {
|
|||||||
return configuration;
|
return configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defaultSort != null && StringUtils.equals(defaultSort.getMetadataField(), sortField)) {
|
|
||||||
return defaultSort;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (DiscoverySortFieldConfiguration sortFieldConfiguration : CollectionUtils.emptyIfNull(sortFields)) {
|
for (DiscoverySortFieldConfiguration sortFieldConfiguration : CollectionUtils.emptyIfNull(sortFields)) {
|
||||||
if (StringUtils.equals(sortFieldConfiguration.getMetadataField(), sortField)) {
|
if (StringUtils.equals(sortFieldConfiguration.getMetadataField(), sortField)) {
|
||||||
return sortFieldConfiguration;
|
return sortFieldConfiguration;
|
||||||
|
@@ -18,11 +18,18 @@ public class DiscoverySortFieldConfiguration {
|
|||||||
private String metadataField;
|
private String metadataField;
|
||||||
private String type = DiscoveryConfigurationParameters.TYPE_TEXT;
|
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() {
|
public String getMetadataField() {
|
||||||
return metadataField;
|
return metadataField;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired(required = true)
|
|
||||||
public void setMetadataField(String metadataField) {
|
public void setMetadataField(String metadataField) {
|
||||||
this.metadataField = metadataField;
|
this.metadataField = metadataField;
|
||||||
}
|
}
|
||||||
@@ -35,6 +42,15 @@ public class DiscoverySortFieldConfiguration {
|
|||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SORT_ORDER getDefaultSortOrder() {
|
||||||
|
return defaultSortOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired(required = true)
|
||||||
|
public void setDefaultSortOrder(SORT_ORDER defaultSortOrder) {
|
||||||
|
this.defaultSortOrder = defaultSortOrder;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (obj != null && obj instanceof DiscoverySortFieldConfiguration) {
|
if (obj != null && obj instanceof DiscoverySortFieldConfiguration) {
|
||||||
|
@@ -47,8 +47,6 @@ BEGIN
|
|||||||
updateseq('fileextension_seq', 'fileextension', 'file_extension_id');
|
updateseq('fileextension_seq', 'fileextension', 'file_extension_id');
|
||||||
updateseq('resourcepolicy_seq', 'resourcepolicy', 'policy_id');
|
updateseq('resourcepolicy_seq', 'resourcepolicy', 'policy_id');
|
||||||
updateseq('workspaceitem_seq', 'workspaceitem', 'workspace_item_id');
|
updateseq('workspaceitem_seq', 'workspaceitem', 'workspace_item_id');
|
||||||
updateseq('workflowitem_seq', 'workflowitem', 'workflow_id');
|
|
||||||
updateseq('tasklistitem_seq', 'tasklistitem', 'tasklist_id');
|
|
||||||
updateseq('registrationdata_seq', 'registrationdata',
|
updateseq('registrationdata_seq', 'registrationdata',
|
||||||
'registrationdata_id');
|
'registrationdata_id');
|
||||||
updateseq('subscription_seq', 'subscription', 'subscription_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('fileextension_seq', max(file_extension_id)) FROM fileextension;
|
||||||
SELECT setval('resourcepolicy_seq', max(policy_id)) FROM resourcepolicy;
|
SELECT setval('resourcepolicy_seq', max(policy_id)) FROM resourcepolicy;
|
||||||
SELECT setval('workspaceitem_seq', max(workspace_item_id)) FROM workspaceitem;
|
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('registrationdata_seq', max(registrationdata_id)) FROM registrationdata;
|
||||||
SELECT setval('subscription_seq', max(subscription_id)) FROM subscription;
|
SELECT setval('subscription_seq', max(subscription_id)) FROM subscription;
|
||||||
SELECT setval('metadatafieldregistry_seq', max(metadata_field_id)) FROM metadatafieldregistry;
|
SELECT setval('metadatafieldregistry_seq', max(metadata_field_id)) FROM metadatafieldregistry;
|
||||||
|
@@ -46,9 +46,11 @@
|
|||||||
<required></required>
|
<required></required>
|
||||||
</field>
|
</field>
|
||||||
</row>
|
</row>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form name="traditionalpageone">
|
<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>
|
<row>
|
||||||
<relation-field>
|
<relation-field>
|
||||||
<relationship-type>isAuthorOfPublication</relationship-type>
|
<relationship-type>isAuthorOfPublication</relationship-type>
|
||||||
@@ -182,7 +184,7 @@ it, please enter the types and the actual numbers or codes.</hint>
|
|||||||
</field>
|
</field>
|
||||||
</row>
|
</row>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form name="traditionalpagetwo">
|
<form name="traditionalpagetwo">
|
||||||
<row>
|
<row>
|
||||||
<field>
|
<field>
|
||||||
@@ -310,7 +312,7 @@ it, please enter the types and the actual numbers or codes.</hint>
|
|||||||
</field>
|
</field>
|
||||||
</row>
|
</row>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form name="sampleauthority">
|
<form name="sampleauthority">
|
||||||
<row>
|
<row>
|
||||||
<field>
|
<field>
|
||||||
@@ -347,7 +349,7 @@ it, please enter the types and the actual numbers or codes.</hint>
|
|||||||
<required></required>
|
<required></required>
|
||||||
</field>
|
</field>
|
||||||
</row>
|
</row>
|
||||||
</form>
|
</form>
|
||||||
</form-definitions>
|
</form-definitions>
|
||||||
|
|
||||||
|
|
||||||
|
@@ -12,9 +12,11 @@ import static junit.framework.TestCase.assertTrue;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.apache.commons.cli.ParseException;
|
import org.apache.commons.cli.ParseException;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.dspace.AbstractIntegrationTestWithDatabase;
|
import org.dspace.AbstractIntegrationTestWithDatabase;
|
||||||
import org.dspace.app.launcher.ScriptLauncher;
|
import org.dspace.app.launcher.ScriptLauncher;
|
||||||
import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler;
|
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.Collection;
|
||||||
import org.dspace.content.Community;
|
import org.dspace.content.Community;
|
||||||
import org.dspace.content.Item;
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.core.Constants;
|
||||||
import org.dspace.scripts.DSpaceRunnable;
|
import org.dspace.scripts.DSpaceRunnable;
|
||||||
import org.dspace.scripts.configuration.ScriptConfiguration;
|
import org.dspace.scripts.configuration.ScriptConfiguration;
|
||||||
import org.dspace.scripts.factory.ScriptServiceFactory;
|
import org.dspace.scripts.factory.ScriptServiceFactory;
|
||||||
@@ -100,4 +103,148 @@ public class MetadataExportIT
|
|||||||
script.run();
|
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>
|
<parent>
|
||||||
<artifactId>dspace-parent</artifactId>
|
<artifactId>dspace-parent</artifactId>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<relativePath>..</relativePath>
|
<relativePath>..</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-parent</artifactId>
|
<artifactId>dspace-parent</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<relativePath>..</relativePath>
|
<relativePath>..</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-rest</artifactId>
|
<artifactId>dspace-rest</artifactId>
|
||||||
<packaging>war</packaging>
|
<packaging>war</packaging>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<name>DSpace (Deprecated) REST Webapp</name>
|
<name>DSpace (Deprecated) REST Webapp</name>
|
||||||
<description>DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED.
|
<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>
|
Please consider using the REST API in the dspace-server-webapp instead!</description>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-parent</artifactId>
|
<artifactId>dspace-parent</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<relativePath>..</relativePath>
|
<relativePath>..</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-parent</artifactId>
|
<artifactId>dspace-parent</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<relativePath>..</relativePath>
|
<relativePath>..</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@@ -14,6 +14,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.dspace.app.rest.model.AuthnRest;
|
import org.dspace.app.rest.model.AuthnRest;
|
||||||
|
import org.dspace.authenticate.ShibAuthentication;
|
||||||
import org.dspace.core.Utils;
|
import org.dspace.core.Utils;
|
||||||
import org.dspace.services.ConfigurationService;
|
import org.dspace.services.ConfigurationService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -27,10 +28,27 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
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 Andrea Bollini (andrea dot bollini at 4science dot it)
|
||||||
* @author Giuseppe Digilio (giuseppe dot digilio 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")
|
@RequestMapping(value = "/api/" + AuthnRest.CATEGORY + "/shibboleth")
|
||||||
@RestController
|
@RestController
|
||||||
@@ -56,7 +74,11 @@ public class ShibbolethRestController implements InitializingBean {
|
|||||||
@RequestMapping(method = RequestMethod.GET)
|
@RequestMapping(method = RequestMethod.GET)
|
||||||
public void shibboleth(HttpServletResponse response,
|
public void shibboleth(HttpServletResponse response,
|
||||||
@RequestParam(name = "redirectUrl", required = false) String redirectUrl) throws IOException {
|
@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");
|
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;
|
package org.dspace.app.rest.authorization.impl;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
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.AuthorizationFeature;
|
||||||
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
|
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
|
||||||
import org.dspace.app.rest.model.BaseObjectRest;
|
import org.dspace.app.rest.model.BaseObjectRest;
|
||||||
import org.dspace.app.rest.model.CollectionRest;
|
import org.dspace.app.rest.model.CollectionRest;
|
||||||
|
import org.dspace.app.rest.model.ItemRest;
|
||||||
import org.dspace.app.rest.utils.Utils;
|
import org.dspace.app.rest.utils.Utils;
|
||||||
import org.dspace.authorize.service.AuthorizeService;
|
import org.dspace.authorize.service.AuthorizeService;
|
||||||
import org.dspace.content.Collection;
|
import org.dspace.content.Collection;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.service.CollectionService;
|
||||||
|
import org.dspace.content.service.ItemService;
|
||||||
import org.dspace.core.Constants;
|
import org.dspace.core.Constants;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.discovery.SearchServiceException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
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.
|
* Authorization is granted if the current user has ADD and WRITE permissions on the given Collection.
|
||||||
*/
|
*/
|
||||||
@Component
|
@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")
|
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
|
@Autowired
|
||||||
private AuthorizeService authorizeService;
|
private AuthorizeService authorizeService;
|
||||||
@@ -39,6 +48,12 @@ public class ManageMappedItemsFeature implements AuthorizationFeature {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private Utils utils;
|
private Utils utils;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ItemService itemService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CollectionService collectionService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
|
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
|
||||||
if (object instanceof CollectionRest) {
|
if (object instanceof CollectionRest) {
|
||||||
@@ -49,13 +64,26 @@ public class ManageMappedItemsFeature implements AuthorizationFeature {
|
|||||||
return true;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getSupportedTypes() {
|
public String[] getSupportedTypes() {
|
||||||
return new String[]{
|
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.
|
* current user is the object's admin. Otherwise, authorization is granted if the current user can view the object.
|
||||||
*/
|
*/
|
||||||
@Component
|
@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")
|
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
|
@Autowired
|
||||||
private ConfigurationService configurationService;
|
private ConfigurationService configurationService;
|
@@ -11,6 +11,7 @@ import java.util.List;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.dspace.app.rest.model.SearchConfigurationRest;
|
import org.dspace.app.rest.model.SearchConfigurationRest;
|
||||||
import org.dspace.app.rest.projection.Projection;
|
import org.dspace.app.rest.projection.Projection;
|
||||||
import org.dspace.discovery.configuration.DiscoveryConfiguration;
|
import org.dspace.discovery.configuration.DiscoveryConfiguration;
|
||||||
@@ -36,7 +37,6 @@ public class DiscoverConfigurationConverter
|
|||||||
addSearchFilters(searchConfigurationRest,
|
addSearchFilters(searchConfigurationRest,
|
||||||
configuration.getSearchFilters(), configuration.getSidebarFacets());
|
configuration.getSearchFilters(), configuration.getSidebarFacets());
|
||||||
addSortOptions(searchConfigurationRest, configuration.getSearchSortConfiguration());
|
addSortOptions(searchConfigurationRest, configuration.getSearchSortConfiguration());
|
||||||
setDefaultSortOption(configuration, searchConfigurationRest);
|
|
||||||
}
|
}
|
||||||
return searchConfigurationRest;
|
return searchConfigurationRest;
|
||||||
}
|
}
|
||||||
@@ -46,23 +46,6 @@ public class DiscoverConfigurationConverter
|
|||||||
return DiscoveryConfiguration.class;
|
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,
|
public void addSearchFilters(SearchConfigurationRest searchConfigurationRest,
|
||||||
List<DiscoverySearchFilter> searchFilterList,
|
List<DiscoverySearchFilter> searchFilterList,
|
||||||
List<DiscoverySearchFilterFacet> facetList) {
|
List<DiscoverySearchFilterFacet> facetList) {
|
||||||
@@ -88,8 +71,13 @@ public class DiscoverConfigurationConverter
|
|||||||
for (DiscoverySortFieldConfiguration discoverySearchSortConfiguration : CollectionUtils
|
for (DiscoverySortFieldConfiguration discoverySearchSortConfiguration : CollectionUtils
|
||||||
.emptyIfNull(searchSortConfiguration.getSortFields())) {
|
.emptyIfNull(searchSortConfiguration.getSortFields())) {
|
||||||
SearchConfigurationRest.SortOption sortOption = new SearchConfigurationRest.SortOption();
|
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.setActualName(discoverySearchSortConfiguration.getType());
|
||||||
|
sortOption.setSortOrder(discoverySearchSortConfiguration.getDefaultSortOrder().name());
|
||||||
searchConfigurationRest.addSortOption(sortOption);
|
searchConfigurationRest.addSortOption(sortOption);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,18 +11,17 @@ import static org.springframework.web.servlet.DispatcherServlet.EXCEPTION_ATTRIB
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.dspace.app.rest.security.RestAuthenticationService;
|
|
||||||
import org.dspace.app.rest.utils.ContextUtil;
|
import org.dspace.app.rest.utils.ContextUtil;
|
||||||
import org.dspace.authorize.AuthorizeException;
|
import org.dspace.authorize.AuthorizeException;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
import org.springframework.beans.TypeMismatchException;
|
import org.springframework.beans.TypeMismatchException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.data.repository.support.QueryMethodParameterConversionException;
|
import org.springframework.data.repository.support.QueryMethodParameterConversionException;
|
||||||
import org.springframework.http.HttpHeaders;
|
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);
|
private static final Set<Integer> LOG_AS_ERROR = Set.of(422);
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RestAuthenticationService restAuthenticationService;
|
|
||||||
|
|
||||||
@ExceptionHandler({AuthorizeException.class, RESTAuthorizationException.class, AccessDeniedException.class})
|
@ExceptionHandler({AuthorizeException.class, RESTAuthorizationException.class, AccessDeniedException.class})
|
||||||
protected void handleAuthorizeException(HttpServletRequest request, HttpServletResponse response, Exception ex)
|
protected void handleAuthorizeException(HttpServletRequest request, HttpServletResponse response, Exception ex)
|
||||||
throws IOException {
|
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);
|
sendErrorResponse(request, response, ex, "Access is denied", HttpServletResponse.SC_FORBIDDEN);
|
||||||
} else {
|
} else {
|
||||||
sendErrorResponse(request, response, ex, "Authentication is required", HttpServletResponse.SC_UNAUTHORIZED);
|
sendErrorResponse(request, response, ex, "Authentication is required", HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
@@ -119,6 +116,14 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH
|
|||||||
HttpStatus.UNPROCESSABLE_ENTITY.value());
|
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.
|
* 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
|
* 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
|
@JsonIgnore
|
||||||
private String actualName;
|
private String actualName;
|
||||||
private String name;
|
private String name;
|
||||||
|
private String sortOrder;
|
||||||
|
|
||||||
public void setActualName(String name) {
|
public void setActualName(String name) {
|
||||||
this.actualName = name;
|
this.actualName = name;
|
||||||
@@ -264,6 +265,14 @@ public class SearchConfigurationRest extends BaseObjectRest<String> {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSortOrder() {
|
||||||
|
return sortOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSortOrder(String sortOrder) {
|
||||||
|
this.sortOrder = sortOrder;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object object) {
|
public boolean equals(Object object) {
|
||||||
return (object instanceof SearchConfigurationRest.SortOption &&
|
return (object instanceof SearchConfigurationRest.SortOption &&
|
||||||
|
@@ -12,6 +12,7 @@ import java.util.Map;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import org.dspace.app.rest.utils.URLUtils;
|
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.Page;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
@@ -102,6 +103,8 @@ public class EmbeddedPageHeader {
|
|||||||
if (page != null) {
|
if (page != null) {
|
||||||
// replace existing page & size params (if exist), otherwise append them
|
// replace existing page & size params (if exist), otherwise append them
|
||||||
uriComp = uriComp.replaceQueryParam("page", page);
|
uriComp = uriComp.replaceQueryParam("page", page);
|
||||||
|
}
|
||||||
|
if (size != Utils.DEFAULT_PAGE_SIZE) {
|
||||||
uriComp = uriComp.replaceQueryParam("size", size);
|
uriComp = uriComp.replaceQueryParam("size", size);
|
||||||
}
|
}
|
||||||
return new Href(uriComp.build().toUriString());
|
return new Href(uriComp.build().toUriString());
|
||||||
|
@@ -190,22 +190,22 @@ public class MetadataFieldRestRepository extends DSpaceRestRepository<MetadataFi
|
|||||||
"forming schema.element.qualifier metadata field name");
|
"forming schema.element.qualifier metadata field name");
|
||||||
}
|
}
|
||||||
filterQueries.add(searchService.toFilterQuery(context, MetadataFieldIndexFactoryImpl.FIELD_NAME_VARIATIONS,
|
filterQueries.add(searchService.toFilterQuery(context, MetadataFieldIndexFactoryImpl.FIELD_NAME_VARIATIONS,
|
||||||
OPERATOR_EQUALS, query).getFilterQuery() + "*");
|
OPERATOR_EQUALS, query, null).getFilterQuery() + "*");
|
||||||
}
|
}
|
||||||
if (StringUtils.isNotBlank(schemaName)) {
|
if (StringUtils.isNotBlank(schemaName)) {
|
||||||
filterQueries.add(
|
filterQueries.add(
|
||||||
searchService.toFilterQuery(context, MetadataFieldIndexFactoryImpl.SCHEMA_FIELD_NAME, OPERATOR_EQUALS,
|
searchService.toFilterQuery(context, MetadataFieldIndexFactoryImpl.SCHEMA_FIELD_NAME, OPERATOR_EQUALS,
|
||||||
schemaName).getFilterQuery());
|
schemaName, null).getFilterQuery());
|
||||||
}
|
}
|
||||||
if (StringUtils.isNotBlank(elementName)) {
|
if (StringUtils.isNotBlank(elementName)) {
|
||||||
filterQueries.add(
|
filterQueries.add(
|
||||||
searchService.toFilterQuery(context, MetadataFieldIndexFactoryImpl.ELEMENT_FIELD_NAME, OPERATOR_EQUALS,
|
searchService.toFilterQuery(context, MetadataFieldIndexFactoryImpl.ELEMENT_FIELD_NAME, OPERATOR_EQUALS,
|
||||||
elementName).getFilterQuery());
|
elementName, null).getFilterQuery());
|
||||||
}
|
}
|
||||||
if (StringUtils.isNotBlank(qualifierName)) {
|
if (StringUtils.isNotBlank(qualifierName)) {
|
||||||
filterQueries.add(searchService
|
filterQueries.add(searchService
|
||||||
.toFilterQuery(context, MetadataFieldIndexFactoryImpl.QUALIFIER_FIELD_NAME, OPERATOR_EQUALS,
|
.toFilterQuery(context, MetadataFieldIndexFactoryImpl.QUALIFIER_FIELD_NAME, OPERATOR_EQUALS,
|
||||||
qualifierName).getFilterQuery());
|
qualifierName, null).getFilterQuery());
|
||||||
}
|
}
|
||||||
|
|
||||||
DiscoverQuery discoverQuery = new DiscoverQuery();
|
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
|
* Checks the current request for a valid authentication token. If found, extracts that token and obtains the
|
||||||
* currently logged in EPerson.
|
* currently logged in EPerson.
|
||||||
* @param request current request
|
* @param request current request
|
||||||
|
* @param request current response
|
||||||
* @param context current DSpace Context
|
* @param context current DSpace Context
|
||||||
* @return EPerson of the logged in user (if auth token found), or null if no auth token is found
|
* @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
|
* 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.
|
* existing authentication data/token is destroyed/invalidated and cannot be reused in later requests.
|
||||||
* <P>
|
* <P>
|
||||||
* In other words, this method invalidates the authentication data created by addAuthenticationDataForUser().
|
* 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 request current request
|
||||||
* @param response current response
|
* @param response current response
|
||||||
* @param context current DSpace Context.
|
* @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
|
* 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
|
* it has been used. This ensures the auth Cookie is temporary in nature, and is destroyed as soon as it is no
|
||||||
* longer needed.
|
* longer needed.
|
||||||
|
* @param request current request
|
||||||
* @param res current response (where Cookie should be destroyed)
|
* @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.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.dspace.authenticate.ShibAuthentication;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.ProviderNotFoundException;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
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)
|
* @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 {
|
public class ShibbolethAuthenticationFilter extends StatelessLoginFilter {
|
||||||
|
|
||||||
@@ -33,7 +40,16 @@ public class ShibbolethAuthenticationFilter extends StatelessLoginFilter {
|
|||||||
@Override
|
@Override
|
||||||
public Authentication attemptAuthentication(HttpServletRequest req,
|
public Authentication attemptAuthentication(HttpServletRequest req,
|
||||||
HttpServletResponse res) throws AuthenticationException {
|
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(
|
return authenticationManager.authenticate(
|
||||||
new DSpaceAuthentication(null, null, new ArrayList<>())
|
new DSpaceAuthentication(null, null, new ArrayList<>())
|
||||||
);
|
);
|
||||||
@@ -44,8 +60,14 @@ public class ShibbolethAuthenticationFilter extends StatelessLoginFilter {
|
|||||||
HttpServletResponse res,
|
HttpServletResponse res,
|
||||||
FilterChain chain,
|
FilterChain chain,
|
||||||
Authentication auth) throws IOException, ServletException {
|
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;
|
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);
|
restAuthenticationService.addAuthenticationDataForUser(req, res, dSpaceAuthentication, true);
|
||||||
chain.doFilter(req, res);
|
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
|
* 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 Frederic Van Reet (frederic dot vanreet at atmire dot com)
|
||||||
* @author Tom Desair (tom dot desair 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);
|
log.error("Access is denied (status:{})", HttpServletResponse.SC_FORBIDDEN, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// If we have a valid Authentication, save it to Spring Security
|
||||||
if (authentication != null) {
|
if (authentication != null) {
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
restAuthenticationService.invalidateAuthenticationCookie(res);
|
|
||||||
}
|
}
|
||||||
chain.doFilter(req, res);
|
chain.doFilter(req, res);
|
||||||
}
|
}
|
||||||
@@ -123,7 +124,7 @@ public class StatelessAuthenticationFilter extends BasicAuthenticationFilter {
|
|||||||
|
|
||||||
Context context = ContextUtil.obtainContext(request);
|
Context context = ContextUtil.obtainContext(request);
|
||||||
|
|
||||||
EPerson eperson = restAuthenticationService.getAuthenticatedEPerson(request, context);
|
EPerson eperson = restAuthenticationService.getAuthenticatedEPerson(request, res, context);
|
||||||
if (eperson != null) {
|
if (eperson != null) {
|
||||||
//Pass the eperson ID to the request service
|
//Pass the eperson ID to the request service
|
||||||
requestService.setCurrentUserId(eperson.getID());
|
requestService.setCurrentUserId(eperson.getID());
|
||||||
|
@@ -23,7 +23,10 @@ import org.springframework.security.web.authentication.AbstractAuthenticationPro
|
|||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
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 Frederic Van Reet (frederic dot vanreet at atmire dot com)
|
||||||
* @author Tom Desair (tom dot desair 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;
|
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
|
@Override
|
||||||
public Authentication attemptAuthentication(HttpServletRequest req,
|
public Authentication attemptAuthentication(HttpServletRequest req,
|
||||||
HttpServletResponse res) throws AuthenticationException {
|
HttpServletResponse res) throws AuthenticationException {
|
||||||
@@ -53,12 +69,28 @@ public class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter
|
|||||||
String user = req.getParameter("user");
|
String user = req.getParameter("user");
|
||||||
String password = req.getParameter("password");
|
String password = req.getParameter("password");
|
||||||
|
|
||||||
|
// Attempt to authenticate by passing user & password (if provided) to AuthenticationProvider class(es)
|
||||||
return authenticationManager.authenticate(
|
return authenticationManager.authenticate(
|
||||||
new DSpaceAuthentication(user, password, new ArrayList<>())
|
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
|
@Override
|
||||||
protected void successfulAuthentication(HttpServletRequest req,
|
protected void successfulAuthentication(HttpServletRequest req,
|
||||||
HttpServletResponse res,
|
HttpServletResponse res,
|
||||||
@@ -69,6 +101,16 @@ public class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter
|
|||||||
restAuthenticationService.addAuthenticationDataForUser(req, res, dSpaceAuthentication, false);
|
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
|
@Override
|
||||||
protected void unsuccessfulAuthentication(HttpServletRequest request,
|
protected void unsuccessfulAuthentication(HttpServletRequest request,
|
||||||
HttpServletResponse response, AuthenticationException failed)
|
HttpServletResponse response, AuthenticationException failed)
|
||||||
|
@@ -89,10 +89,7 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
|||||||
context.commit();
|
context.commit();
|
||||||
|
|
||||||
// Add newly generated auth token to the response
|
// Add newly generated auth token to the response
|
||||||
addTokenToResponse(response, token, addCookie);
|
addTokenToResponse(request, response, token, addCookie);
|
||||||
|
|
||||||
// Reset our CSRF token, generating a new one
|
|
||||||
resetCSRFToken(request, response);
|
|
||||||
|
|
||||||
} catch (JOSEException e) {
|
} catch (JOSEException e) {
|
||||||
log.error("JOSE Exception", e);
|
log.error("JOSE Exception", e);
|
||||||
@@ -125,9 +122,9 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EPerson getAuthenticatedEPerson(HttpServletRequest request, Context context) {
|
public EPerson getAuthenticatedEPerson(HttpServletRequest request, HttpServletResponse response, Context context) {
|
||||||
try {
|
try {
|
||||||
String token = getLoginToken(request);
|
String token = getLoginToken(request, response);
|
||||||
EPerson ePerson = null;
|
EPerson ePerson = null;
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
token = getShortLivedToken(request);
|
token = getShortLivedToken(request);
|
||||||
@@ -156,22 +153,29 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
|||||||
@Override
|
@Override
|
||||||
public void invalidateAuthenticationData(HttpServletRequest request, HttpServletResponse response,
|
public void invalidateAuthenticationData(HttpServletRequest request, HttpServletResponse response,
|
||||||
Context context) throws Exception {
|
Context context) throws Exception {
|
||||||
String token = getLoginToken(request);
|
String token = getLoginToken(request, response);
|
||||||
invalidateAuthenticationCookie(response);
|
|
||||||
loginJWTTokenHandler.invalidateToken(token, request, context);
|
loginJWTTokenHandler.invalidateToken(token, request, context);
|
||||||
|
|
||||||
// Reset our CSRF token, generating a new one
|
// Reset our CSRF token, generating a new one
|
||||||
resetCSRFToken(request, response);
|
resetCSRFToken(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate our temporary authentication cookie by overwriting it in the response.
|
||||||
|
* @param request
|
||||||
|
* @param response
|
||||||
|
*/
|
||||||
@Override
|
@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
|
// Re-send the same cookie (as addTokenToResponse()) with no value and a Max-Age of 0 seconds
|
||||||
ResponseCookie cookie = ResponseCookie.from(AUTHORIZATION_COOKIE, "")
|
ResponseCookie cookie = ResponseCookie.from(AUTHORIZATION_COOKIE, "")
|
||||||
.maxAge(0).httpOnly(true).secure(true).sameSite("None").build();
|
.maxAge(0).httpOnly(true).secure(true).sameSite("None").build();
|
||||||
|
|
||||||
// Write the cookie to the Set-Cookie header in order to send it
|
// Write the cookie to the Set-Cookie header in order to send it
|
||||||
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
||||||
|
|
||||||
|
// Reset our CSRF token, generating a new one
|
||||||
|
resetCSRFToken(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -179,6 +183,18 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
|||||||
return authenticationService;
|
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
|
@Override
|
||||||
public String getWwwAuthenticateHeaderValue(final HttpServletRequest request, final HttpServletResponse response) {
|
public String getWwwAuthenticateHeaderValue(final HttpServletRequest request, final HttpServletResponse response) {
|
||||||
Iterator<AuthenticationMethod> authenticationMethodIterator
|
Iterator<AuthenticationMethod> authenticationMethodIterator
|
||||||
@@ -195,10 +211,12 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
|||||||
|
|
||||||
wwwAuthenticate.append(authenticationMethod.getName()).append(" realm=\"DSpace REST API\"");
|
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);
|
String loginPageURL = authenticationMethod.loginPageURL(context, request, response);
|
||||||
if (org.apache.commons.lang3.StringUtils.isNotBlank(loginPageURL)) {
|
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("\"");
|
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>
|
* <P>
|
||||||
* If 'addCookie' is true, then the JWT is also added to a response cookie. This is primarily for support of auth
|
* 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
|
* 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).
|
* sent via HTTPS (Secure).
|
||||||
* <P>
|
* <P>
|
||||||
* If 'addCookie' is false, then the JWT is only added in the Authorization header. This is recommended behavior
|
* If 'addCookie' is false, then the JWT is only added in the Authorization header & the auth cookie (if it exists)
|
||||||
* as it is the most secure. For the UI (or any JS clients) the JWT must be sent in the Authorization header.
|
* 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 response current response
|
||||||
* @param token the authentication token
|
* @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) {
|
private void addTokenToResponse(final HttpServletRequest request, final HttpServletResponse response,
|
||||||
// we need authentication cookies because Shibboleth can't use the authentication headers due to the redirects
|
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) {
|
if (addCookie) {
|
||||||
ResponseCookie cookie = ResponseCookie.from(AUTHORIZATION_COOKIE, token)
|
ResponseCookie cookie = ResponseCookie.from(AUTHORIZATION_COOKIE, token)
|
||||||
.httpOnly(true).secure(true).sameSite("None").build();
|
.httpOnly(true).secure(true).sameSite("None").build();
|
||||||
|
|
||||||
// Write the cookie to the Set-Cookie header in order to send it
|
// Write the cookie to the Set-Cookie header in order to send it
|
||||||
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
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));
|
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 tokenValue = null;
|
||||||
String authHeader = request.getHeader(AUTHORIZATION_HEADER);
|
String authHeader = request.getHeader(AUTHORIZATION_HEADER);
|
||||||
String authCookie = getAuthorizationCookie(request);
|
String authCookie = getAuthorizationCookie(request);
|
||||||
@@ -254,6 +297,11 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
|||||||
return tokenValue;
|
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) {
|
private String getAuthorizationCookie(HttpServletRequest request) {
|
||||||
String authCookie = "";
|
String authCookie = "";
|
||||||
Cookie[] cookies = request.getCookies();
|
Cookie[] cookies = request.getCookies();
|
||||||
@@ -261,12 +309,26 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
|||||||
for (Cookie cookie : cookies) {
|
for (Cookie cookie : cookies) {
|
||||||
if (cookie.getName().equals(AUTHORIZATION_COOKIE) && StringUtils.isNotEmpty(cookie.getValue())) {
|
if (cookie.getName().equals(AUTHORIZATION_COOKIE) && StringUtils.isNotEmpty(cookie.getValue())) {
|
||||||
authCookie = cookie.getValue();
|
authCookie = cookie.getValue();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return authCookie;
|
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.
|
* 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
|
* 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;
|
package org.dspace.app.rest.submit.factory.impl;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
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.MetadataValueRest;
|
||||||
import org.dspace.app.rest.model.patch.LateObjectEvaluator;
|
import org.dspace.app.rest.model.patch.LateObjectEvaluator;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
import org.dspace.content.InProgressSubmission;
|
import org.dspace.content.InProgressSubmission;
|
||||||
import org.dspace.content.Item;
|
import org.dspace.content.Item;
|
||||||
import org.dspace.content.MetadataValue;
|
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.ItemService;
|
||||||
|
import org.dspace.content.service.RelationshipService;
|
||||||
|
import org.dspace.core.Constants;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.core.Utils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@@ -66,9 +78,18 @@ import org.springframework.util.Assert;
|
|||||||
*/
|
*/
|
||||||
public class ItemMetadataValueAddPatchOperation extends MetadataValueAddPatchOperation<Item> {
|
public class ItemMetadataValueAddPatchOperation extends MetadataValueAddPatchOperation<Item> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* log4j category
|
||||||
|
*/
|
||||||
|
private static final Logger log =
|
||||||
|
org.apache.logging.log4j.LogManager.getLogger(ItemMetadataValueAddPatchOperation.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
ItemService itemService;
|
ItemService itemService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
RelationshipService relationshipService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void add(Context context, HttpServletRequest currentRequest, InProgressSubmission source, String path, Object value)
|
void add(Context context, HttpServletRequest currentRequest, InProgressSubmission source, String path, Object value)
|
||||||
throws SQLException {
|
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
|
@Override
|
||||||
protected ItemService getDSpaceObjectService() {
|
protected ItemService getDSpaceObjectService() {
|
||||||
return itemService;
|
return itemService;
|
||||||
|
@@ -14,12 +14,14 @@ import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.dspace.app.rest.converter.query.SearchQueryConverter;
|
import org.dspace.app.rest.converter.query.SearchQueryConverter;
|
||||||
import org.dspace.app.rest.exception.DSpaceBadRequestException;
|
import org.dspace.app.rest.exception.DSpaceBadRequestException;
|
||||||
|
import org.dspace.app.rest.exception.InvalidSearchRequestException;
|
||||||
import org.dspace.app.rest.parameter.SearchFilter;
|
import org.dspace.app.rest.parameter.SearchFilter;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
import org.dspace.core.LogManager;
|
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
|
//Load defaults if we did not receive values
|
||||||
if (sortBy == null) {
|
if (sortBy == null) {
|
||||||
sortBy = getDefaultSortField(searchSortConfiguration);
|
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) {
|
private String getDefaultSortDirection(DiscoverySortConfiguration searchSortConfiguration, String sortOrder) {
|
||||||
if (searchSortConfiguration != null) {
|
if (Objects.nonNull(searchSortConfiguration.getSortFields()) &&
|
||||||
sortOrder = searchSortConfiguration.getDefaultSortOrder()
|
!searchSortConfiguration.getSortFields().isEmpty()) {
|
||||||
.toString();
|
sortOrder = searchSortConfiguration.getSortFields().get(0).getDefaultSortOrder().name();
|
||||||
}
|
}
|
||||||
return sortOrder;
|
return sortOrder;
|
||||||
}
|
}
|
||||||
@@ -332,8 +343,12 @@ public class DiscoverQueryBuilder implements InitializingBean {
|
|||||||
private String getDefaultSortField(DiscoverySortConfiguration searchSortConfiguration) {
|
private String getDefaultSortField(DiscoverySortConfiguration searchSortConfiguration) {
|
||||||
String sortBy;// Attempt to find the default one, if none found we use SCORE
|
String sortBy;// Attempt to find the default one, if none found we use SCORE
|
||||||
sortBy = "score";
|
sortBy = "score";
|
||||||
if (searchSortConfiguration != null && searchSortConfiguration.getDefaultSort() != null) {
|
if (Objects.nonNull(searchSortConfiguration.getSortFields()) &&
|
||||||
DiscoverySortFieldConfiguration defaultSort = searchSortConfiguration.getDefaultSort();
|
!searchSortConfiguration.getSortFields().isEmpty()) {
|
||||||
|
DiscoverySortFieldConfiguration defaultSort = searchSortConfiguration.getSortFields().get(0);
|
||||||
|
if (StringUtils.isBlank(defaultSort.getMetadataField())) {
|
||||||
|
return sortBy;
|
||||||
|
}
|
||||||
sortBy = defaultSort.getMetadataField();
|
sortBy = defaultSort.getMetadataField();
|
||||||
}
|
}
|
||||||
return sortBy;
|
return sortBy;
|
||||||
@@ -378,7 +393,8 @@ public class DiscoverQueryBuilder implements InitializingBean {
|
|||||||
DiscoverFilterQuery filterQuery = searchService.toFilterQuery(context,
|
DiscoverFilterQuery filterQuery = searchService.toFilterQuery(context,
|
||||||
filter.getIndexFieldName(),
|
filter.getIndexFieldName(),
|
||||||
searchFilter.getOperator(),
|
searchFilter.getOperator(),
|
||||||
searchFilter.getValue());
|
searchFilter.getValue(),
|
||||||
|
discoveryConfiguration);
|
||||||
|
|
||||||
if (filterQuery != null) {
|
if (filterQuery != null) {
|
||||||
filterQueries.add(filterQuery.getFilterQuery());
|
filterQueries.add(filterQuery.getFilterQuery());
|
||||||
|
@@ -108,7 +108,7 @@ public class Utils {
|
|||||||
/**
|
/**
|
||||||
* The default page size, if unspecified in the request.
|
* 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.
|
* 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.
|
# However, you may wish to set this to "always" in your 'local.cfg' for development or debugging purposes.
|
||||||
server.error.include-stacktrace = never
|
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
|
# Spring Boot Autoconfigure
|
||||||
#
|
#
|
||||||
@@ -107,6 +119,9 @@ spring.main.allow-bean-definition-overriding = true
|
|||||||
# Log4J configuration
|
# Log4J configuration
|
||||||
logging.config = ${dspace.dir}/config/log4j2.xml
|
logging.config = ${dspace.dir}/config/log4j2.xml
|
||||||
|
|
||||||
|
##################################
|
||||||
|
# Spring MVC file upload settings
|
||||||
|
#
|
||||||
# Maximum size of a single uploaded file (default = 1MB)
|
# Maximum size of a single uploaded file (default = 1MB)
|
||||||
spring.servlet.multipart.max-file-size = 512MB
|
spring.servlet.multipart.max-file-size = 512MB
|
||||||
|
|
||||||
|
@@ -98,39 +98,62 @@
|
|||||||
"onclick" : function() { toastr.remove(); }
|
"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({
|
$.ajax({
|
||||||
url : window.location.href.replace("login.html", "") + 'api/authn/login',
|
url : window.location.href.replace("login.html", "") + 'api/authn/status',
|
||||||
type : 'POST',
|
type : 'GET',
|
||||||
beforeSend: function (xhr, settings) {
|
success : function(result, status, xhr) {
|
||||||
// If CSRF token found in cookie, send it back as X-XSRF-Token header
|
// Check for an update to the CSRF Token & save to a MyHalBrowserCsrfToken cookie (if found)
|
||||||
var csrfToken = getCSRFToken();
|
checkForUpdatedCSRFTokenInResponse(xhr);
|
||||||
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);
|
|
||||||
|
|
||||||
// If 401 Unauthorized, check WWW-Authenticate for authentication options
|
// Check for WWW-Authenticate header. If found, this means we are not yet authenticated, and
|
||||||
if (xhr.status === 401) {
|
// therefore we need to display available authentication options.
|
||||||
var authenticate = xhr.getResponseHeader("WWW-Authenticate");
|
var authenticate = xhr.getResponseHeader("WWW-Authenticate");
|
||||||
var element = $('div.other-login-methods');
|
if (authenticate !== null) {
|
||||||
if(authenticate !== null) {
|
var element = $('div.other-login-methods');
|
||||||
var realms = authenticate.match(/(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g);
|
var realms = authenticate.match(/(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g);
|
||||||
if (realms.length == 1){
|
if (realms.length == 1){
|
||||||
var loc = /location="([^,]*)"/.exec(authenticate);
|
var loc = /location="([^,]*)"/.exec(authenticate);
|
||||||
if (loc !== null && loc.length === 2) {
|
if (loc !== null && loc.length === 2) {
|
||||||
document.location = loc[1];
|
document.location = loc[1];
|
||||||
}
|
}
|
||||||
} else if (realms.length > 1){
|
} else if (realms.length > 1){
|
||||||
for (var i = 0; i < realms.length; i++){
|
for (var i = 0; i < realms.length; i++){
|
||||||
addLocationButton(realms[i], element);
|
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) {
|
$("#login-form").submit(function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@@ -191,7 +216,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
success : successHandler,
|
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');
|
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.hamcrest.Matchers.startsWith;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotEquals;
|
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.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.get;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
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.cookie;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
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.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -164,35 +166,111 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStatusShibAuthenticatedWithCookie() throws Exception {
|
public void testStatusShibAuthenticatedWithCookie() throws Exception {
|
||||||
//Enable Shibboleth login
|
//Enable Shibboleth login only
|
||||||
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
|
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
|
||||||
|
|
||||||
//Simulate that a shibboleth authentication has happened
|
String uiURL = configurationService.getProperty("dspace.ui.url");
|
||||||
String token = getClient().perform(post("/api/authn/login")
|
|
||||||
|
// 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-MAIL", eperson.getEmail())
|
||||||
.requestAttr("SHIB-SCOPED-AFFILIATION", "faculty;staff"))
|
.requestAttr("SHIB-SCOPED-AFFILIATION", "faculty;staff"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().is3xxRedirection())
|
||||||
.andReturn().getResponse().getHeader(AUTHORIZATION_HEADER).replace("Bearer ", "");
|
.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];
|
// Verify the temporary cookie now exists & obtain its token for use below
|
||||||
cookies[0] = new Cookie(AUTHORIZATION_COOKIE, token);
|
assertNotNull(authCookie);
|
||||||
|
String token = authCookie.getValue();
|
||||||
|
|
||||||
//Check if we are authenticated with a status request with authorization cookie
|
// This step is _not required_ to successfully authenticate, but it mocks the behavior of our UI & HAL Browser.
|
||||||
getClient().perform(get("/api/authn/status")
|
// We'll send a "/status" request to the REST API with our auth cookie. This should return that we have a
|
||||||
.secure(true)
|
// *valid* authentication (as auth cookie is valid), however the cookie will remain. To complete the login
|
||||||
.cookie(cookies))
|
// 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())
|
.andExpect(status().isOk())
|
||||||
//We expect the content type to be "application/hal+json;charset=UTF-8"
|
|
||||||
.andExpect(content().contentType(contentType))
|
.andExpect(content().contentType(contentType))
|
||||||
.andExpect(jsonPath("$.okay", is(true)))
|
.andExpect(jsonPath("$.okay", is(true)))
|
||||||
.andExpect(jsonPath("$.authenticated", is(true)))
|
.andExpect(jsonPath("$.authenticated", is(true)))
|
||||||
.andExpect(jsonPath("$.type", is("status")));
|
.andExpect(jsonPath("$.type", is("status")));
|
||||||
|
|
||||||
//Logout
|
//Logout, invalidating the token
|
||||||
getClient(token).perform(post("/api/authn/logout"))
|
getClient(token).perform(post("/api/authn/logout").header("Origin", uiURL))
|
||||||
.andExpect(status().isNoContent());
|
.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
|
@Test
|
||||||
public void testStatusPasswordAuthenticatedWithCookie() throws Exception {
|
public void testStatusPasswordAuthenticatedWithCookie() throws Exception {
|
||||||
// Login via password to retrieve a valid token
|
// 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
|
// Remove "Bearer " from that token, so that we are left with the token itself
|
||||||
token = token.replace("Bearer ", "");
|
token = token.replace("Bearer ", "");
|
||||||
|
|
||||||
// Save token to an Authorization cookie
|
// Fake the creation of an auth cookie, just for testing. (Currently, it's not possible to create an auth cookie
|
||||||
Cookie[] cookies = new Cookie[1];
|
// via Password auth, but this test proves it would work if enabled)
|
||||||
cookies[0] = new Cookie(AUTHORIZATION_COOKIE, token);
|
Cookie authCookie = new Cookie(AUTHORIZATION_COOKIE, token);
|
||||||
|
|
||||||
//Check if we are authenticated with a status request using authorization cookie
|
// Now, similar to how both the UI & Hal Browser authentication works, send a "/status" request to the REST API
|
||||||
getClient().perform(get("/api/authn/status")
|
// with our auth cookie. This should return that we *have a valid* authentication (in the auth cookie).
|
||||||
.secure(true)
|
// However, this is just a validation check, so this auth cookie will remain. To complete the login process
|
||||||
.cookie(cookies))
|
// 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())
|
.andExpect(status().isOk())
|
||||||
//We expect the content type to be "application/hal+json"
|
|
||||||
.andExpect(content().contentType(contentType))
|
.andExpect(content().contentType(contentType))
|
||||||
.andExpect(jsonPath("$.okay", is(true)))
|
.andExpect(jsonPath("$.okay", is(true)))
|
||||||
.andExpect(jsonPath("$.authenticated", is(true)))
|
.andExpect(jsonPath("$.authenticated", is(true)))
|
||||||
.andExpect(jsonPath("$.type", is("status")));
|
.andExpect(jsonPath("$.type", is("status")))
|
||||||
//Logout
|
// 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"))
|
getClient(token).perform(post("/api/authn/logout"))
|
||||||
.andExpect(status().isNoContent());
|
.andExpect(status().isNoContent());
|
||||||
}
|
}
|
||||||
@@ -1087,7 +1193,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
|
|||||||
String loginToken = getAuthToken(eperson.getEmail(), password);
|
String loginToken = getAuthToken(eperson.getEmail(), password);
|
||||||
getClient().perform(get("/api/core/bitstreams/" + bitstream.getID()
|
getClient().perform(get("/api/core/bitstreams/" + bitstream.getID()
|
||||||
+ "/content?authentication-token=" + loginToken))
|
+ "/content?authentication-token=" + loginToken))
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1098,7 +1204,7 @@ public class AuthenticationRestControllerIT extends AbstractControllerIntegratio
|
|||||||
Thread.sleep(1);
|
Thread.sleep(1);
|
||||||
getClient().perform(get("/api/core/bitstreams/" + bitstream.getID()
|
getClient().perform(get("/api/core/bitstreams/" + bitstream.getID()
|
||||||
+ "/content?authentication-token=" + shortLivedToken))
|
+ "/content?authentication-token=" + shortLivedToken))
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@@ -2478,4 +2478,118 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
|
|||||||
getClient().perform(get("/api/core/communities/search/findAdminAuthorized"))
|
getClient().perform(get("/api/core/communities/search/findAdminAuthorized"))
|
||||||
.andExpect(status().isUnauthorized());
|
.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.WorkspaceItem;
|
||||||
import org.dspace.content.authority.Choices;
|
import org.dspace.content.authority.Choices;
|
||||||
import org.dspace.content.authority.service.MetadataAuthorityService;
|
import org.dspace.content.authority.service.MetadataAuthorityService;
|
||||||
|
import org.dspace.discovery.configuration.DiscoverySortFieldConfiguration;
|
||||||
import org.dspace.eperson.EPerson;
|
import org.dspace.eperson.EPerson;
|
||||||
import org.dspace.eperson.Group;
|
import org.dspace.eperson.Group;
|
||||||
import org.dspace.services.ConfigurationService;
|
import org.dspace.services.ConfigurationService;
|
||||||
@@ -988,14 +989,74 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
|
|||||||
SearchFilterMatcher.isJournalOfPublicationRelation()
|
SearchFilterMatcher.isJournalOfPublicationRelation()
|
||||||
)))
|
)))
|
||||||
//These sortOptions need to be present as it's the default in the configuration
|
//These sortOptions need to be present as it's the default in the configuration
|
||||||
.andExpect(jsonPath("$.sortOptions", containsInAnyOrder(
|
.andExpect(jsonPath("$.sortOptions", contains(
|
||||||
SortOptionMatcher.titleSortOption(),
|
SortOptionMatcher.sortOptionMatcher(
|
||||||
SortOptionMatcher.dateIssuedSortOption(),
|
"score", DiscoverySortFieldConfiguration.SORT_ORDER.desc.name()),
|
||||||
SortOptionMatcher.dateAccessionedSortOption(),
|
SortOptionMatcher.sortOptionMatcher(
|
||||||
SortOptionMatcher.scoreSortOption()
|
"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
|
@Test
|
||||||
public void discoverSearchObjectsTest() throws Exception {
|
public void discoverSearchObjectsTest() throws Exception {
|
||||||
|
|
||||||
@@ -5177,6 +5238,137 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest
|
|||||||
.andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")));
|
.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
|
@Test
|
||||||
public void discoverSearchPoolTaskObjectsTest() throws Exception {
|
public void discoverSearchPoolTaskObjectsTest() throws Exception {
|
||||||
context.turnOffAuthorisationSystem();
|
context.turnOffAuthorisationSystem();
|
||||||
|
@@ -25,6 +25,7 @@ import java.sql.SQLException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import org.dspace.app.rest.matcher.MetadataMatcher;
|
import org.dspace.app.rest.matcher.MetadataMatcher;
|
||||||
import org.dspace.app.rest.model.MetadataValueRest;
|
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.ItemService;
|
||||||
import org.dspace.content.service.RelationshipTypeService;
|
import org.dspace.content.service.RelationshipTypeService;
|
||||||
import org.dspace.content.service.WorkspaceItemService;
|
import org.dspace.content.service.WorkspaceItemService;
|
||||||
|
import org.hamcrest.Matcher;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -81,6 +83,8 @@ public class PatchMetadataIT extends AbstractEntityIntegrationTest {
|
|||||||
|
|
||||||
private List<String> authorsOriginalOrder;
|
private List<String> authorsOriginalOrder;
|
||||||
|
|
||||||
|
private List<MetadataValue> authorsMetadataOriginalOrder;
|
||||||
|
|
||||||
private AtomicReference<Integer> idRef1;
|
private AtomicReference<Integer> idRef1;
|
||||||
private AtomicReference<Integer> idRef2;
|
private AtomicReference<Integer> idRef2;
|
||||||
|
|
||||||
@@ -202,19 +206,19 @@ public class PatchMetadataIT extends AbstractEntityIntegrationTest {
|
|||||||
.andDo(result -> idRef2.set(read(result.getResponse().getContentAsString(), "$.id")));
|
.andDo(result -> idRef2.set(read(result.getResponse().getContentAsString(), "$.id")));
|
||||||
|
|
||||||
publication = workspaceItemService.find(context, publicationItem.getID());
|
publication = workspaceItemService.find(context, publicationItem.getID());
|
||||||
List<MetadataValue> publicationAuthorList =
|
authorsMetadataOriginalOrder =
|
||||||
itemService.getMetadata(publication.getItem(), "dc", "contributor", "author", Item.ANY);
|
itemService.getMetadata(publication.getItem(), "dc", "contributor", "author", Item.ANY);
|
||||||
assertEquals(publicationAuthorList.size(), 5);
|
assertEquals(authorsMetadataOriginalOrder.size(), 5);
|
||||||
assertThat(publicationAuthorList.get(0).getValue(), equalTo(authorsOriginalOrder.get(0)));
|
assertThat(authorsMetadataOriginalOrder.get(0).getValue(), equalTo(authorsOriginalOrder.get(0)));
|
||||||
assertThat(publicationAuthorList.get(0).getAuthority(), not(startsWith("virtual::")));
|
assertThat(authorsMetadataOriginalOrder.get(0).getAuthority(), not(startsWith("virtual::")));
|
||||||
assertThat(publicationAuthorList.get(1).getValue(), equalTo(authorsOriginalOrder.get(1)));
|
assertThat(authorsMetadataOriginalOrder.get(1).getValue(), equalTo(authorsOriginalOrder.get(1)));
|
||||||
assertThat(publicationAuthorList.get(1).getAuthority(), startsWith("virtual::"));
|
assertThat(authorsMetadataOriginalOrder.get(1).getAuthority(), startsWith("virtual::"));
|
||||||
assertThat(publicationAuthorList.get(2).getValue(), equalTo(authorsOriginalOrder.get(2)));
|
assertThat(authorsMetadataOriginalOrder.get(2).getValue(), equalTo(authorsOriginalOrder.get(2)));
|
||||||
assertThat(publicationAuthorList.get(2).getAuthority(), not(startsWith("virtual::")));
|
assertThat(authorsMetadataOriginalOrder.get(2).getAuthority(), not(startsWith("virtual::")));
|
||||||
assertThat(publicationAuthorList.get(3).getValue(), equalTo(authorsOriginalOrder.get(3)));
|
assertThat(authorsMetadataOriginalOrder.get(3).getValue(), equalTo(authorsOriginalOrder.get(3)));
|
||||||
assertThat(publicationAuthorList.get(3).getAuthority(), not(startsWith("virtual::")));
|
assertThat(authorsMetadataOriginalOrder.get(3).getAuthority(), not(startsWith("virtual::")));
|
||||||
assertThat(publicationAuthorList.get(4).getValue(), equalTo(authorsOriginalOrder.get(4)));
|
assertThat(authorsMetadataOriginalOrder.get(4).getValue(), equalTo(authorsOriginalOrder.get(4)));
|
||||||
assertThat(publicationAuthorList.get(4).getAuthority(), startsWith("virtual::"));
|
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
|
* Create a move operation on a workspace item's "traditionalpageone" section for
|
||||||
* metadata field "dc.contributor.author".
|
* metadata field "dc.contributor.author".
|
||||||
|
@@ -27,58 +27,96 @@ public class ShibbolethRestControllerIT extends AbstractControllerIntegrationTes
|
|||||||
@Autowired
|
@Autowired
|
||||||
ConfigurationService configurationService;
|
ConfigurationService configurationService;
|
||||||
|
|
||||||
|
public static final String[] PASS_ONLY = {"org.dspace.authenticate.PasswordAuthentication"};
|
||||||
|
public static final String[] SHIB_ONLY = {"org.dspace.authenticate.ShibAuthentication"};
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
|
// Add a second trusted host for some tests
|
||||||
configurationService.setProperty("rest.cors.allowed-origins",
|
configurationService.setProperty("rest.cors.allowed-origins",
|
||||||
"${dspace.ui.url}, http://anotherdspacehost:4000");
|
"${dspace.ui.url}, http://anotherdspacehost:4000");
|
||||||
|
|
||||||
|
// Enable Shibboleth login for all tests
|
||||||
|
configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRedirectToDefaultDspaceUrl() throws Exception {
|
public void testRedirectToDefaultDspaceUrl() throws Exception {
|
||||||
String token = getAuthToken(eperson.getEmail(), password);
|
// 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.
|
||||||
getClient(token).perform(get("/api/authn/shibboleth"))
|
// 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(status().is3xxRedirection())
|
||||||
.andExpect(redirectedUrl("http://localhost:4000"));
|
.andExpect(redirectedUrl("http://localhost:4000"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRedirectToGivenTrustedUrl() throws Exception {
|
public void testRedirectToGivenTrustedUrl() throws Exception {
|
||||||
|
getClient().perform(get("/api/authn/shibboleth")
|
||||||
String token = getAuthToken(eperson.getEmail(), password);
|
.param("redirectUrl", "http://localhost:8080/server/api/authn/status")
|
||||||
|
.requestAttr("SHIB-MAIL", eperson.getEmail()))
|
||||||
getClient(token).perform(get("/api/authn/shibboleth")
|
|
||||||
.param("redirectUrl", "http://localhost:8080/server/api/authn/status"))
|
|
||||||
.andExpect(status().is3xxRedirection())
|
.andExpect(status().is3xxRedirection())
|
||||||
.andExpect(redirectedUrl("http://localhost:8080/server/api/authn/status"));
|
.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
|
@Test
|
||||||
public void testRedirectToAnotherGivenTrustedUrl() throws Exception {
|
public void testRedirectToAnotherGivenTrustedUrl() throws Exception {
|
||||||
String token = getAuthToken(eperson.getEmail(), password);
|
String token = getAuthToken(eperson.getEmail(), password);
|
||||||
|
|
||||||
getClient(token).perform(get("/api/authn/shibboleth")
|
getClient().perform(get("/api/authn/shibboleth")
|
||||||
.param("redirectUrl", "http://anotherdspacehost:4000/home"))
|
.param("redirectUrl", "http://anotherdspacehost:4000/home")
|
||||||
|
.requestAttr("SHIB-MAIL", eperson.getEmail()))
|
||||||
.andExpect(status().is3xxRedirection())
|
.andExpect(status().is3xxRedirection())
|
||||||
.andExpect(redirectedUrl("http://anotherdspacehost:4000/home"));
|
.andExpect(redirectedUrl("http://anotherdspacehost:4000/home"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRedirectToGivenUntrustedUrl() throws Exception {
|
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.
|
// Should result in a 400 error.
|
||||||
getClient(token).perform(get("/api/authn/shibboleth")
|
getClient().perform(get("/api/authn/shibboleth")
|
||||||
.param("redirectUrl", "http://dspace.org"))
|
.param("redirectUrl", "http://dspace.org")
|
||||||
.andExpect(status().isBadRequest());
|
.requestAttr("SHIB-MAIL", eperson.getEmail()))
|
||||||
|
.andExpect(status().isBadRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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"))
|
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(
|
getClient("unvalidToken").perform(
|
||||||
get("/api/statistics/usagereports/" + itemNotVisitedWithBitstreams.getID() + "_" + TOTAL_VISITS_REPORT_ID))
|
get("/api/statistics/usagereports/" + itemNotVisitedWithBitstreams.getID() + "_" + TOTAL_VISITS_REPORT_ID))
|
||||||
// ** THEN **
|
// ** THEN **
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -829,7 +829,7 @@ public class StatisticsRestRepositoryIT extends AbstractControllerIntegrationTes
|
|||||||
.perform(get("/api/statistics/usagereports/search/object?uri=http://localhost:8080/server/api/core" +
|
.perform(get("/api/statistics/usagereports/search/object?uri=http://localhost:8080/server/api/core" +
|
||||||
"/items/" + itemNotVisitedWithBitstreams.getID()))
|
"/items/" + itemNotVisitedWithBitstreams.getID()))
|
||||||
// ** THEN **
|
// ** THEN **
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@@ -49,8 +49,8 @@ public class WorkflowActionRestRepositoryIT extends AbstractControllerIntegratio
|
|||||||
String token = "nonValidToken";
|
String token = "nonValidToken";
|
||||||
//When we call this facets endpoint
|
//When we call this facets endpoint
|
||||||
getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT))
|
getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT))
|
||||||
//We expect a 403 Forbidden status
|
//We expect a 401 Unauthorized status
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -112,8 +112,8 @@ public class WorkflowActionRestRepositoryIT extends AbstractControllerIntegratio
|
|||||||
WorkflowActionConfig existentWorkflow = xmlWorkflowFactory.getActionByName(nameActionWithOptions);
|
WorkflowActionConfig existentWorkflow = xmlWorkflowFactory.getActionByName(nameActionWithOptions);
|
||||||
//When we call this facets endpoint
|
//When we call this facets endpoint
|
||||||
getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT + "/" + nameActionWithOptions))
|
getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT + "/" + nameActionWithOptions))
|
||||||
//We expect a 403 Forbidden status
|
//We expect a 401 Unauthorized status
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@@ -121,8 +121,8 @@ public class WorkflowDefinitionRestRepositoryIT extends AbstractControllerIntegr
|
|||||||
String token = "NonValidToken";
|
String token = "NonValidToken";
|
||||||
//When we call this facets endpoint
|
//When we call this facets endpoint
|
||||||
getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT))
|
getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT))
|
||||||
//We expect a 403 Forbidden status
|
//We expect a 401 Unauthorized status
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -192,8 +192,8 @@ public class WorkflowDefinitionRestRepositoryIT extends AbstractControllerIntegr
|
|||||||
String workflowName = defaultWorkflow.getID();
|
String workflowName = defaultWorkflow.getID();
|
||||||
//When we call this facets endpoint
|
//When we call this facets endpoint
|
||||||
getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + workflowName))
|
getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + workflowName))
|
||||||
//We expect a 403 Forbidden status
|
//We expect a 401 Unauthorized status
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -402,8 +402,8 @@ public class WorkflowDefinitionRestRepositoryIT extends AbstractControllerIntegr
|
|||||||
//When we call this facets endpoint
|
//When we call this facets endpoint
|
||||||
getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + defaultWorkflow.getID()
|
getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + defaultWorkflow.getID()
|
||||||
+ "/collections"))
|
+ "/collections"))
|
||||||
//We expect a 403 Forbidden status
|
//We expect a 401 Unauthorized status
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -441,8 +441,8 @@ public class WorkflowDefinitionRestRepositoryIT extends AbstractControllerIntegr
|
|||||||
//When we call this facets endpoint
|
//When we call this facets endpoint
|
||||||
getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + defaultWorkflow.getID()
|
getClient(token).perform(get(WORKFLOW_DEFINITIONS_ENDPOINT + "/" + defaultWorkflow.getID()
|
||||||
+ "/steps"))
|
+ "/steps"))
|
||||||
//We expect a 403 Forbidden status
|
//We expect a 401 Unauthorized status
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@@ -47,8 +47,8 @@ public class WorkflowStepRestRepositoryIT extends AbstractControllerIntegrationT
|
|||||||
String token = "NonValidToken";
|
String token = "NonValidToken";
|
||||||
//When we call this facets endpoint
|
//When we call this facets endpoint
|
||||||
getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT))
|
getClient(token).perform(get(WORKFLOW_ACTIONS_ENDPOINT))
|
||||||
//We expect a 403 Forbidden status
|
//We expect a 401 Unauthorized status
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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.codec.CharEncoding;
|
||||||
import org.apache.commons.io.IOUtils;
|
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.BitstreamConverter;
|
||||||
import org.dspace.app.rest.converter.CollectionConverter;
|
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.BitstreamRest;
|
||||||
import org.dspace.app.rest.model.CollectionRest;
|
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.projection.Projection;
|
||||||
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
|
||||||
import org.dspace.app.rest.utils.Utils;
|
import org.dspace.app.rest.utils.Utils;
|
||||||
|
import org.dspace.authorize.service.AuthorizeService;
|
||||||
import org.dspace.builder.BitstreamBuilder;
|
import org.dspace.builder.BitstreamBuilder;
|
||||||
import org.dspace.builder.BundleBuilder;
|
import org.dspace.builder.BundleBuilder;
|
||||||
import org.dspace.builder.CollectionBuilder;
|
import org.dspace.builder.CollectionBuilder;
|
||||||
import org.dspace.builder.CommunityBuilder;
|
import org.dspace.builder.CommunityBuilder;
|
||||||
|
import org.dspace.builder.EPersonBuilder;
|
||||||
import org.dspace.builder.ItemBuilder;
|
import org.dspace.builder.ItemBuilder;
|
||||||
import org.dspace.builder.ResourcePolicyBuilder;
|
import org.dspace.builder.ResourcePolicyBuilder;
|
||||||
import org.dspace.content.Bitstream;
|
import org.dspace.content.Bitstream;
|
||||||
@@ -36,14 +43,16 @@ import org.dspace.content.Collection;
|
|||||||
import org.dspace.content.Community;
|
import org.dspace.content.Community;
|
||||||
import org.dspace.content.Item;
|
import org.dspace.content.Item;
|
||||||
import org.dspace.core.Constants;
|
import org.dspace.core.Constants;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
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
|
@Autowired
|
||||||
private Utils utils;
|
private Utils utils;
|
||||||
@@ -54,15 +63,27 @@ public class ManageMappedItemsFeatureIT extends AbstractControllerIntegrationTes
|
|||||||
@Autowired
|
@Autowired
|
||||||
private BitstreamConverter bitstreamConverter;
|
private BitstreamConverter bitstreamConverter;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ItemConverter itemConverter;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthorizeService authorizeService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthorizationFeatureService authorizationFeatureService;
|
||||||
|
|
||||||
|
private EPerson userA;
|
||||||
private Community communityA;
|
private Community communityA;
|
||||||
private Collection collectionA;
|
private Collection collectionA;
|
||||||
|
private Collection collectionB;
|
||||||
private CollectionRest collectionARest;
|
private CollectionRest collectionARest;
|
||||||
private Item itemA;
|
private Item itemA;
|
||||||
private Bitstream bitstreamA;
|
private Bitstream bitstreamA;
|
||||||
private BitstreamRest bitstreamARest;
|
private BitstreamRest bitstreamARest;
|
||||||
private Bundle bundleA;
|
private Bundle bundleA;
|
||||||
|
private AuthorizationFeature canManageMappingsFeature;
|
||||||
|
|
||||||
final String feature = "canManageMappedItems";
|
final String feature = "canManageMappings";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Before
|
@Before
|
||||||
@@ -70,12 +91,19 @@ public class ManageMappedItemsFeatureIT extends AbstractControllerIntegrationTes
|
|||||||
super.setUp();
|
super.setUp();
|
||||||
context.turnOffAuthorisationSystem();
|
context.turnOffAuthorisationSystem();
|
||||||
|
|
||||||
|
userA = EPersonBuilder.createEPerson(context)
|
||||||
|
.withEmail("userEmail@test.com")
|
||||||
|
.withPassword(password).build();
|
||||||
|
|
||||||
communityA = CommunityBuilder.createCommunity(context)
|
communityA = CommunityBuilder.createCommunity(context)
|
||||||
.withName("communityA")
|
.withName("communityA")
|
||||||
.build();
|
.build();
|
||||||
collectionA = CollectionBuilder.createCollection(context, communityA)
|
collectionA = CollectionBuilder.createCollection(context, communityA)
|
||||||
.withName("collectionA")
|
.withName("collectionA")
|
||||||
.build();
|
.build();
|
||||||
|
collectionB = CollectionBuilder.createCollection(context, communityA)
|
||||||
|
.withName("collectionB")
|
||||||
|
.build();
|
||||||
itemA = ItemBuilder.createItem(context, collectionA)
|
itemA = ItemBuilder.createItem(context, collectionA)
|
||||||
.withTitle("itemA")
|
.withTitle("itemA")
|
||||||
.build();
|
.build();
|
||||||
@@ -88,7 +116,7 @@ public class ManageMappedItemsFeatureIT extends AbstractControllerIntegrationTes
|
|||||||
.withName("bistreamA")
|
.withName("bistreamA")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
canManageMappingsFeature = authorizationFeatureService.find(CanManageMappingsFeature.NAME);
|
||||||
context.restoreAuthSystemState();
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
collectionARest = collectionConverter.convert(collectionA, Projection.DEFAULT);
|
collectionARest = collectionConverter.convert(collectionA, Projection.DEFAULT);
|
||||||
@@ -188,4 +216,65 @@ public class ManageMappedItemsFeatureIT extends AbstractControllerIntegrationTes
|
|||||||
.andExpect(jsonPath("$.page.totalElements", is(0)))
|
.andExpect(jsonPath("$.page.totalElements", is(0)))
|
||||||
.andExpect(jsonPath("$._embedded").doesNotExist());
|
.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;
|
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
|
@Autowired
|
||||||
private Utils utils;
|
private Utils utils;
|
||||||
@@ -68,7 +68,7 @@ public class ViewVersionsFeatureIT extends AbstractControllerIntegrationTest {
|
|||||||
private BitstreamRest bitstreamARest;
|
private BitstreamRest bitstreamARest;
|
||||||
private Bundle bundleA;
|
private Bundle bundleA;
|
||||||
|
|
||||||
final String feature = "canViewVersions";
|
final String feature = "canSeeVersions";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Before
|
@Before
|
@@ -99,9 +99,11 @@ public class DiscoverConfigurationConverterTest {
|
|||||||
DiscoverySortFieldConfiguration discoverySortFieldConfiguration = new DiscoverySortFieldConfiguration();
|
DiscoverySortFieldConfiguration discoverySortFieldConfiguration = new DiscoverySortFieldConfiguration();
|
||||||
discoverySortFieldConfiguration.setMetadataField("title");
|
discoverySortFieldConfiguration.setMetadataField("title");
|
||||||
discoverySortFieldConfiguration.setType("text");
|
discoverySortFieldConfiguration.setType("text");
|
||||||
|
discoverySortFieldConfiguration.setDefaultSortOrder(DiscoverySortFieldConfiguration.SORT_ORDER.asc);
|
||||||
DiscoverySortFieldConfiguration discoverySortFieldConfiguration1 = new DiscoverySortFieldConfiguration();
|
DiscoverySortFieldConfiguration discoverySortFieldConfiguration1 = new DiscoverySortFieldConfiguration();
|
||||||
discoverySortFieldConfiguration1.setMetadataField("author");
|
discoverySortFieldConfiguration1.setMetadataField("author");
|
||||||
discoverySortFieldConfiguration1.setType("text");
|
discoverySortFieldConfiguration1.setType("text");
|
||||||
|
discoverySortFieldConfiguration1.setDefaultSortOrder(DiscoverySortFieldConfiguration.SORT_ORDER.asc);
|
||||||
LinkedList<DiscoverySortFieldConfiguration> mockedList = new LinkedList<>();
|
LinkedList<DiscoverySortFieldConfiguration> mockedList = new LinkedList<>();
|
||||||
mockedList.add(discoverySortFieldConfiguration);
|
mockedList.add(discoverySortFieldConfiguration);
|
||||||
mockedList.add(discoverySortFieldConfiguration1);
|
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();
|
Map<String,Object> links = embeddedPageHeader.getLinks();
|
||||||
// "self" should be same as URL
|
// "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.
|
// "first" should not exist, as we are on the first page.
|
||||||
assertFalse(links.containsKey("first"));
|
assertFalse(links.containsKey("first"));
|
||||||
// "prev" should not exist, as we are on the first page.
|
// "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 static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
@@ -35,6 +36,7 @@ import java.util.List;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.dspace.app.rest.exception.DSpaceBadRequestException;
|
import org.dspace.app.rest.exception.DSpaceBadRequestException;
|
||||||
|
import org.dspace.app.rest.exception.InvalidSearchRequestException;
|
||||||
import org.dspace.app.rest.parameter.SearchFilter;
|
import org.dspace.app.rest.parameter.SearchFilter;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
import org.dspace.discovery.DiscoverFacetField;
|
import org.dspace.discovery.DiscoverFacetField;
|
||||||
@@ -111,7 +113,8 @@ public class DiscoverQueryBuilderTest {
|
|||||||
any(), any(DiscoverQuery.class)))
|
any(), any(DiscoverQuery.class)))
|
||||||
.then(invocation -> new FacetYearRange((DiscoverySearchFilterFacet) invocation.getArguments()[2]));
|
.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],
|
.then(invocation -> new DiscoverFilterQuery((String) invocation.getArguments()[1],
|
||||||
invocation.getArguments()[1] + ":\"" + invocation.getArguments()[3] + "\"",
|
invocation.getArguments()[1] + ":\"" + invocation.getArguments()[3] + "\"",
|
||||||
(String) invocation.getArguments()[3]));
|
(String) invocation.getArguments()[3]));
|
||||||
@@ -139,17 +142,23 @@ public class DiscoverQueryBuilderTest {
|
|||||||
discoveryConfiguration.setHitHighlightingConfiguration(discoveryHitHighlightingConfiguration);
|
discoveryConfiguration.setHitHighlightingConfiguration(discoveryHitHighlightingConfiguration);
|
||||||
|
|
||||||
|
|
||||||
DiscoverySortConfiguration sortConfiguration = new DiscoverySortConfiguration();
|
|
||||||
|
|
||||||
DiscoverySortFieldConfiguration defaultSort = new DiscoverySortFieldConfiguration();
|
DiscoverySortFieldConfiguration defaultSort = new DiscoverySortFieldConfiguration();
|
||||||
defaultSort.setMetadataField("dc.date.accessioned");
|
defaultSort.setMetadataField("dc.date.accessioned");
|
||||||
defaultSort.setType(DiscoveryConfigurationParameters.TYPE_DATE);
|
defaultSort.setType(DiscoveryConfigurationParameters.TYPE_DATE);
|
||||||
sortConfiguration.setDefaultSort(defaultSort);
|
defaultSort.setDefaultSortOrder(DiscoverySortFieldConfiguration.SORT_ORDER.desc);
|
||||||
sortConfiguration.setDefaultSortOrder(DiscoverySortConfiguration.SORT_ORDER.desc);
|
|
||||||
|
|
||||||
|
List<DiscoverySortFieldConfiguration> listSortField = new ArrayList<DiscoverySortFieldConfiguration>();
|
||||||
|
listSortField.add(defaultSort);
|
||||||
|
|
||||||
|
DiscoverySortConfiguration sortConfiguration = new DiscoverySortConfiguration();
|
||||||
|
|
||||||
DiscoverySortFieldConfiguration titleSort = new DiscoverySortFieldConfiguration();
|
DiscoverySortFieldConfiguration titleSort = new DiscoverySortFieldConfiguration();
|
||||||
titleSort.setMetadataField("dc.title");
|
titleSort.setMetadataField("dc.title");
|
||||||
sortConfiguration.setSortFields(Arrays.asList(titleSort));
|
titleSort.setDefaultSortOrder(DiscoverySortFieldConfiguration.SORT_ORDER.asc);
|
||||||
|
listSortField.add(titleSort);
|
||||||
|
|
||||||
|
sortConfiguration.setSortFields(listSortField);
|
||||||
|
|
||||||
discoveryConfiguration.setSearchSortConfiguration(sortConfiguration);
|
discoveryConfiguration.setSearchSortConfiguration(sortConfiguration);
|
||||||
|
|
||||||
@@ -266,7 +275,7 @@ public class DiscoverQueryBuilderTest {
|
|||||||
.buildQuery(context, scope, discoveryConfiguration, query, Arrays.asList(searchFilter), "TEST", page);
|
.buildQuery(context, scope, discoveryConfiguration, query, Arrays.asList(searchFilter), "TEST", page);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = DSpaceBadRequestException.class)
|
@Test(expected = InvalidSearchRequestException.class)
|
||||||
public void testInvalidSortField() throws Exception {
|
public void testInvalidSortField() throws Exception {
|
||||||
page = PageRequest.of(2, 10, Sort.Direction.ASC, "test");
|
page = PageRequest.of(2, 10, Sort.Direction.ASC, "test");
|
||||||
queryBuilder
|
queryBuilder
|
||||||
@@ -283,7 +292,8 @@ public class DiscoverQueryBuilderTest {
|
|||||||
|
|
||||||
@Test(expected = DSpaceBadRequestException.class)
|
@Test(expected = DSpaceBadRequestException.class)
|
||||||
public void testInvalidSearchFilter2() throws Exception {
|
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);
|
.thenThrow(SQLException.class);
|
||||||
|
|
||||||
queryBuilder
|
queryBuilder
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-parent</artifactId>
|
<artifactId>dspace-parent</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
@@ -426,10 +426,9 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
|
|||||||
service = (T) applicationContext.getBean(name, type);
|
service = (T) applicationContext.getBean(name, type);
|
||||||
} catch (BeansException e) {
|
} catch (BeansException e) {
|
||||||
// no luck, try the fall back option
|
// no luck, try the fall back option
|
||||||
log.info(
|
log.warn(
|
||||||
"Unable to locate bean by name or id={}."
|
"Unable to locate bean by name or id={}."
|
||||||
+ " Will try to look up bean by type next."
|
+ " Will try to look up bean by type next.", name, e);
|
||||||
+ " BeansException: {}", name, e.getMessage());
|
|
||||||
service = null;
|
service = null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -438,9 +437,8 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
|
|||||||
service = (T) applicationContext.getBean(type.getName(), type);
|
service = (T) applicationContext.getBean(type.getName(), type);
|
||||||
} catch (BeansException e) {
|
} catch (BeansException e) {
|
||||||
// no luck, try the fall back option
|
// no luck, try the fall back option
|
||||||
log.info("Unable to locate bean by name or id={}."
|
log.warn("Unable to locate bean by name or id={}."
|
||||||
+ " Will try to look up bean by type next."
|
+ " Will try to look up bean by type next.", type.getName(), e);
|
||||||
+ " BeansException: {}", type.getName(), e.getMessage());
|
|
||||||
service = null;
|
service = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-parent</artifactId>
|
<artifactId>dspace-parent</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<relativePath>..</relativePath>
|
<relativePath>..</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-parent</artifactId>
|
<artifactId>dspace-parent</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<relativePath>..</relativePath>
|
<relativePath>..</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@@ -20,11 +20,15 @@
|
|||||||
dspace.dir = /dspace
|
dspace.dir = /dspace
|
||||||
|
|
||||||
# URL of DSpace backend ('server' webapp). Include port number etc.
|
# 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
|
dspace.server.url = http://localhost:8080/server
|
||||||
|
|
||||||
# URL of DSpace frontend (Angular UI). Include port number etc
|
# 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.
|
# 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
|
dspace.ui.url = http://localhost:4000
|
||||||
|
|
||||||
# Name of the site
|
# Name of the site
|
||||||
@@ -120,8 +124,8 @@ db.removeabandonedtimeout = 300
|
|||||||
mail.server = smtp.example.com
|
mail.server = smtp.example.com
|
||||||
|
|
||||||
# SMTP mail server authentication username and password (if required)
|
# SMTP mail server authentication username and password (if required)
|
||||||
mail.server.username =
|
#mail.server.username =
|
||||||
mail.server.password =
|
#mail.server.password =
|
||||||
|
|
||||||
# SMTP mail server alternate port (defaults to 25)
|
# SMTP mail server alternate port (defaults to 25)
|
||||||
mail.server.port = 25
|
mail.server.port = 25
|
||||||
@@ -371,16 +375,6 @@ useProxies = true
|
|||||||
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
|
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
|
||||||
#proxies.trusted.include_ui_ip = true
|
#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 Filter / Format Filter plugins (through PluginService) ####
|
||||||
# Media/Format Filters help to full-text index content or
|
# Media/Format Filters help to full-text index content or
|
||||||
# perform automated format conversions
|
# perform automated format conversions
|
||||||
|
@@ -89,7 +89,7 @@
|
|||||||
<min>0</min>
|
<min>0</min>
|
||||||
</leftCardinality>
|
</leftCardinality>
|
||||||
<rightCardinality>
|
<rightCardinality>
|
||||||
<min>1</min>
|
<min>0</min>
|
||||||
</rightCardinality>
|
</rightCardinality>
|
||||||
</type>
|
</type>
|
||||||
<type>
|
<type>
|
||||||
@@ -101,8 +101,7 @@
|
|||||||
<min>0</min>
|
<min>0</min>
|
||||||
</leftCardinality>
|
</leftCardinality>
|
||||||
<rightCardinality>
|
<rightCardinality>
|
||||||
<min>1</min>
|
<min>0</min>
|
||||||
<max>1</max>
|
|
||||||
</rightCardinality>
|
</rightCardinality>
|
||||||
</type>
|
</type>
|
||||||
<type>
|
<type>
|
||||||
@@ -128,7 +127,6 @@
|
|||||||
</leftCardinality>
|
</leftCardinality>
|
||||||
<rightCardinality>
|
<rightCardinality>
|
||||||
<min>0</min>
|
<min>0</min>
|
||||||
<max>1</max>
|
|
||||||
</rightCardinality>
|
</rightCardinality>
|
||||||
<copyToRight>true</copyToRight>
|
<copyToRight>true</copyToRight>
|
||||||
</type>
|
</type>
|
||||||
|
@@ -17,7 +17,36 @@
|
|||||||
<!-- which does not appear in this map will be associated with the mapping -->
|
<!-- which does not appear in this map will be associated with the mapping -->
|
||||||
<!-- for handle "default". -->
|
<!-- for handle "default". -->
|
||||||
<submission-map>
|
<submission-map>
|
||||||
|
<!-- Default submission process -->
|
||||||
<name-map collection-handle="default" submission-name="traditional"/>
|
<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>
|
</submission-map>
|
||||||
|
|
||||||
|
|
||||||
@@ -44,7 +73,7 @@
|
|||||||
<!-- -->
|
<!-- -->
|
||||||
<step-definitions>
|
<step-definitions>
|
||||||
<!-- The "collection" step is a "special step" which is *REQUIRED* -->
|
<!-- 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. -->
|
<!-- to a collection. This step ensures that a collection is always selected. -->
|
||||||
<step-definition id="collection">
|
<step-definition id="collection">
|
||||||
<heading></heading>
|
<heading></heading>
|
||||||
@@ -52,6 +81,9 @@
|
|||||||
<type>collection</type>
|
<type>collection</type>
|
||||||
<scope visibility="hidden" visibilityOutside="hidden">submission</scope>
|
<scope visibility="hidden" visibilityOutside="hidden">submission</scope>
|
||||||
</step-definition>
|
</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">
|
<step-definition id="traditionalpageone" mandatory="true">
|
||||||
<heading>submit.progressbar.describe.stepone</heading>
|
<heading>submit.progressbar.describe.stepone</heading>
|
||||||
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
<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>
|
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
||||||
<type>submission-form</type>
|
<type>submission-form</type>
|
||||||
</step-definition>
|
</step-definition>
|
||||||
|
<step-definition id="publicationStep" mandatory="true">
|
||||||
<step-definition id="peopleStep" 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>
|
<heading>submit.progressbar.describe.stepone</heading>
|
||||||
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
||||||
<type>submission-form</type>
|
<type>submission-form</type>
|
||||||
@@ -106,38 +142,26 @@
|
|||||||
<scope visibilityOutside="read-only">submission</scope>
|
<scope visibilityOutside="read-only">submission</scope>
|
||||||
</step-definition>
|
</step-definition>
|
||||||
|
|
||||||
<!-- Step Upload Item with Embargo Features to enable this step, please
|
<!-- This optional step may allow the user to select a Creative Commons license -->
|
||||||
make sure to comment-out the previous step "UploadStep" <step-definition id="upload-with-embargo">
|
<step-definition id="cclicense" mandatory="false">
|
||||||
<heading>submit.progressbar.upload</heading> <processing-class>org.dspace.submit.step.UploadWithEmbargoStep</processing-class>
|
<heading>submit.progressbar.CClicense</heading>
|
||||||
<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>
|
|
||||||
<processing-class>org.dspace.app.rest.submit.step.CCLicenseStep</processing-class>
|
<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 -->
|
<!-- This optional step may enrich the current submission using information extracted
|
||||||
<!-- Uncomment this step to enrich the current submission using information extracted
|
from uploaded files or metadata. -->
|
||||||
from uploaded files or metadata. Please note that this step will be triggered only when a request is performed that mean
|
<!-- Please note that this step will be triggered only when a request is performed,
|
||||||
the file to be uploaded or the metadata saved. Angular allows to configure an autosave feature based on a timer or the input
|
e.g. when a file is uploaded or the form is saved. The Angular UI also supports an
|
||||||
of specific metadata such as identifiers used by this step, see the submission.autosave settings in the environment.common.ts
|
autosave feature based on a timer, or the input of specific metadata such as identifiers,
|
||||||
Check also config/spring/api/step-processing-listener.xml for further server side configuration
|
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>
|
<heading>submit.progressbar.ExtractMetadataStep</heading>
|
||||||
<processing-class>org.dspace.app.rest.submit.step.ExtractMetadataStep</processing-class>
|
<processing-class>org.dspace.app.rest.submit.step.ExtractMetadataStep</processing-class>
|
||||||
<type>extract</type>
|
<type>extract</type>
|
||||||
</step-definition> -->
|
</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> -->
|
|
||||||
|
|
||||||
<!-- OpenAIRE submission steps/forms -->
|
<!-- OpenAIRE submission steps/forms -->
|
||||||
<step-definition id="openAIREProjectForm" mandatory="true">
|
<step-definition id="openAIREProjectForm" mandatory="true">
|
||||||
@@ -166,7 +190,7 @@
|
|||||||
<type>submission-form</type>
|
<type>submission-form</type>
|
||||||
</step-definition>
|
</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">
|
<step-definition id="sample">
|
||||||
<heading>Sample</heading>
|
<heading>Sample</heading>
|
||||||
<processing-class>org.dspace.submit.step.SampleStep</processing-class>
|
<processing-class>org.dspace.submit.step.SampleStep</processing-class>
|
||||||
@@ -192,7 +216,6 @@
|
|||||||
|
|
||||||
<!--This "traditional" process defines the DEFAULT item submission process -->
|
<!--This "traditional" process defines the DEFAULT item submission process -->
|
||||||
<submission-process name="traditional">
|
<submission-process name="traditional">
|
||||||
|
|
||||||
<!--Uncommment to display the SAMPLE step as your first step -->
|
<!--Uncommment to display the SAMPLE step as your first step -->
|
||||||
<!--<step id="sample"/> -->
|
<!--<step id="sample"/> -->
|
||||||
|
|
||||||
@@ -204,26 +227,45 @@
|
|||||||
|
|
||||||
<!--Step will be to Upload the item -->
|
<!--Step will be to Upload the item -->
|
||||||
<step id="upload"/>
|
<step id="upload"/>
|
||||||
<!-- <step id="upload-with-embargo"/> -->
|
|
||||||
<!-- <step id="extractionstep"/> -->
|
<!-- <step id="extractionstep"/> -->
|
||||||
|
|
||||||
<!--Step will be to select a Creative Commons License -->
|
<!-- Uncomment this step to allow the user to select a Creative Commons License -->
|
||||||
<!-- Uncomment this step to allow the user to select a Creative Commons -->
|
|
||||||
<!-- <step id="cclicense"/> -->
|
<!-- <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="license"/>
|
||||||
<!-- <step id="creative-commons"/> -->
|
|
||||||
<!-- <step id="verify"/> -->
|
|
||||||
</submission-process>
|
</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="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="upload"/>
|
||||||
<step id="license"/>
|
<step id="license"/>
|
||||||
</submission-process>
|
</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">
|
<submission-process name="Project">
|
||||||
<step id="collection"/>
|
<step id="collection"/>
|
||||||
<step id="projectStep"/>
|
<step id="projectStep"/>
|
||||||
@@ -231,25 +273,44 @@
|
|||||||
<step id="license"/>
|
<step id="license"/>
|
||||||
</submission-process>
|
</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">
|
<submission-process name="OrgUnit">
|
||||||
<step id="collection"/>
|
<step id="collection"/>
|
||||||
<step id="orgUnitStep"/>
|
<step id="orgUnitStep"/>
|
||||||
<step id="upload"/>
|
<step id="upload"/>
|
||||||
<step id="license"/>
|
<step id="license"/>
|
||||||
</submission-process>
|
</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="collection"/>
|
||||||
<step id="journalStep"/>
|
<step id="journalStep"/>
|
||||||
<step id="upload"/>
|
<step id="upload"/>
|
||||||
<step id="license"/>
|
<step id="license"/>
|
||||||
</submission-process>
|
</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="collection"/>
|
||||||
<step id="journalVolumeStep"/>
|
<step id="journalVolumeStep"/>
|
||||||
<step id="upload"/>
|
<step id="upload"/>
|
||||||
<step id="license"/>
|
<step id="license"/>
|
||||||
</submission-process>
|
</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="collection"/>
|
||||||
<step id="journalIssueStep"/>
|
<step id="journalIssueStep"/>
|
||||||
<step id="upload"/>
|
<step id="upload"/>
|
||||||
|
@@ -33,11 +33,15 @@
|
|||||||
dspace.dir=/dspace
|
dspace.dir=/dspace
|
||||||
|
|
||||||
# URL of DSpace backend ('server' webapp). Include port number etc.
|
# 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
|
dspace.server.url = http://localhost:8080/server
|
||||||
|
|
||||||
# URL of DSpace frontend (Angular UI). Include port number etc
|
# 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.
|
# 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
|
dspace.ui.url = http://localhost:4000
|
||||||
|
|
||||||
# Name of the site
|
# Name of the site
|
||||||
@@ -188,6 +192,7 @@ db.schema = public
|
|||||||
#plugin.sequence.org.dspace.authenticate.AuthenticationMethod = org.dspace.authenticate.LDAPAuthentication
|
#plugin.sequence.org.dspace.authenticate.AuthenticationMethod = org.dspace.authenticate.LDAPAuthentication
|
||||||
|
|
||||||
# Shibboleth authentication/authorization. See authentication-shibboleth.cfg for default configuration.
|
# 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
|
#plugin.sequence.org.dspace.authenticate.AuthenticationMethod = org.dspace.authenticate.ShibAuthentication
|
||||||
|
|
||||||
# X.509 certificate authentication. See authentication-x509.cfg for default configuration.
|
# 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).
|
# 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.
|
# Multiple allowed origin URLs may be comma separated. Wildcard value (*) is NOT SUPPORTED.
|
||||||
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
|
# (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}
|
#rest.cors.allowed-origins = ${dspace.ui.url}
|
||||||
|
|
||||||
#################################################
|
#################################################
|
||||||
|
@@ -82,7 +82,7 @@
|
|||||||
<Logger name='org.dspace.services'
|
<Logger name='org.dspace.services'
|
||||||
level='ERROR'/>
|
level='ERROR'/>
|
||||||
<Logger name='org.dspace.servicemanager'
|
<Logger name='org.dspace.servicemanager'
|
||||||
level='ERROR'/>
|
level='WARN'/>
|
||||||
<Logger name='org.dspace.providers'
|
<Logger name='org.dspace.providers'
|
||||||
level='ERROR'/>
|
level='ERROR'/>
|
||||||
<Logger name='org.dspace.utils'
|
<Logger name='org.dspace.utils'
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
<bean class="org.dspace.app.util.MetadataExposureServiceImpl"/>
|
<bean class="org.dspace.app.util.MetadataExposureServiceImpl"/>
|
||||||
<bean class="org.dspace.app.util.OpenSearchServiceImpl"/>
|
<bean class="org.dspace.app.util.OpenSearchServiceImpl"/>
|
||||||
<bean class="org.dspace.app.util.WebAppServiceImpl"/>
|
<bean class="org.dspace.app.util.WebAppServiceImpl"/>
|
||||||
|
<bean class="org.dspace.app.util.DSpaceObjectUtilsImpl"/>
|
||||||
|
|
||||||
<bean class="org.dspace.authenticate.AuthenticationServiceImpl"/>
|
<bean class="org.dspace.authenticate.AuthenticationServiceImpl"/>
|
||||||
|
|
||||||
|
@@ -154,11 +154,9 @@
|
|||||||
<!--The sort filters for the discovery search-->
|
<!--The sort filters for the discovery search-->
|
||||||
<property name="searchSortConfiguration">
|
<property name="searchSortConfiguration">
|
||||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
<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">
|
<property name="sortFields">
|
||||||
<list>
|
<list>
|
||||||
|
<ref bean="sortScore" />
|
||||||
<ref bean="sortTitle" />
|
<ref bean="sortTitle" />
|
||||||
<ref bean="sortDateIssued" />
|
<ref bean="sortDateIssued" />
|
||||||
<ref bean="sortDateAccessioned"/>
|
<ref bean="sortDateAccessioned"/>
|
||||||
@@ -295,11 +293,9 @@
|
|||||||
<!--The sort filters for the discovery search-->
|
<!--The sort filters for the discovery search-->
|
||||||
<property name="searchSortConfiguration">
|
<property name="searchSortConfiguration">
|
||||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
<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">
|
<property name="sortFields">
|
||||||
<list>
|
<list>
|
||||||
|
<ref bean="sortScore" />
|
||||||
<ref bean="sortTitle" />
|
<ref bean="sortTitle" />
|
||||||
<ref bean="sortDateIssued" />
|
<ref bean="sortDateIssued" />
|
||||||
<ref bean="sortDateAccessioned"/>
|
<ref bean="sortDateAccessioned"/>
|
||||||
@@ -441,11 +437,9 @@
|
|||||||
<!--The sort filters for the discovery search-->
|
<!--The sort filters for the discovery search-->
|
||||||
<property name="searchSortConfiguration">
|
<property name="searchSortConfiguration">
|
||||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
<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">
|
<property name="sortFields">
|
||||||
<list>
|
<list>
|
||||||
|
<ref bean="sortScore" />
|
||||||
<ref bean="sortTitle" />
|
<ref bean="sortTitle" />
|
||||||
<ref bean="sortDateIssued" />
|
<ref bean="sortDateIssued" />
|
||||||
<ref bean="sortDateAccessioned"/>
|
<ref bean="sortDateAccessioned"/>
|
||||||
@@ -581,11 +575,9 @@
|
|||||||
<!--The sort filters for the discovery search (same as defaultConfiguration above)-->
|
<!--The sort filters for the discovery search (same as defaultConfiguration above)-->
|
||||||
<property name="searchSortConfiguration">
|
<property name="searchSortConfiguration">
|
||||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
<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">
|
<property name="sortFields">
|
||||||
<list>
|
<list>
|
||||||
|
<ref bean="sortScore" />
|
||||||
<ref bean="sortTitle" />
|
<ref bean="sortTitle" />
|
||||||
<ref bean="sortDateIssued" />
|
<ref bean="sortDateIssued" />
|
||||||
<ref bean="sortDateAccessioned" />
|
<ref bean="sortDateAccessioned" />
|
||||||
@@ -687,11 +679,9 @@
|
|||||||
<!--The sort filters for the discovery search-->
|
<!--The sort filters for the discovery search-->
|
||||||
<property name="searchSortConfiguration">
|
<property name="searchSortConfiguration">
|
||||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
<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">
|
<property name="sortFields">
|
||||||
<list>
|
<list>
|
||||||
|
<ref bean="sortScore" />
|
||||||
<ref bean="sortTitle" />
|
<ref bean="sortTitle" />
|
||||||
<ref bean="sortDateIssued" />
|
<ref bean="sortDateIssued" />
|
||||||
</list>
|
</list>
|
||||||
@@ -764,11 +754,9 @@
|
|||||||
<!--The sort filters for the discovery search-->
|
<!--The sort filters for the discovery search-->
|
||||||
<property name="searchSortConfiguration">
|
<property name="searchSortConfiguration">
|
||||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
<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">
|
<property name="sortFields">
|
||||||
<list>
|
<list>
|
||||||
|
<ref bean="sortScore" />
|
||||||
<ref bean="sortTitle" />
|
<ref bean="sortTitle" />
|
||||||
<ref bean="sortDateIssued" />
|
<ref bean="sortDateIssued" />
|
||||||
</list>
|
</list>
|
||||||
@@ -841,11 +829,9 @@
|
|||||||
<!--The sort filters for the discovery search-->
|
<!--The sort filters for the discovery search-->
|
||||||
<property name="searchSortConfiguration">
|
<property name="searchSortConfiguration">
|
||||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
<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">
|
<property name="sortFields">
|
||||||
<list>
|
<list>
|
||||||
|
<ref bean="sortScore" />
|
||||||
<ref bean="sortTitle" />
|
<ref bean="sortTitle" />
|
||||||
<ref bean="sortDateIssued" />
|
<ref bean="sortDateIssued" />
|
||||||
</list>
|
</list>
|
||||||
@@ -927,11 +913,9 @@
|
|||||||
<!--The sort filters for the discovery search-->
|
<!--The sort filters for the discovery search-->
|
||||||
<property name="searchSortConfiguration">
|
<property name="searchSortConfiguration">
|
||||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
<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">
|
<property name="sortFields">
|
||||||
<list>
|
<list>
|
||||||
|
<ref bean="sortScore" />
|
||||||
<ref bean="sortTitle" />
|
<ref bean="sortTitle" />
|
||||||
<ref bean="sortDateIssued" />
|
<ref bean="sortDateIssued" />
|
||||||
<ref bean="sortDateAccessioned"/>
|
<ref bean="sortDateAccessioned"/>
|
||||||
@@ -989,11 +973,9 @@
|
|||||||
<!--The sort filters for the discovery search-->
|
<!--The sort filters for the discovery search-->
|
||||||
<property name="searchSortConfiguration">
|
<property name="searchSortConfiguration">
|
||||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
<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">
|
<property name="sortFields">
|
||||||
<list>
|
<list>
|
||||||
|
<ref bean="sortScore" />
|
||||||
<ref bean="sortFamilyName"/>
|
<ref bean="sortFamilyName"/>
|
||||||
<ref bean="sortGivenName"/>
|
<ref bean="sortGivenName"/>
|
||||||
<ref bean="sortBirthDate"/>
|
<ref bean="sortBirthDate"/>
|
||||||
@@ -1047,11 +1029,9 @@
|
|||||||
<!--The sort filters for the discovery search-->
|
<!--The sort filters for the discovery search-->
|
||||||
<property name="searchSortConfiguration">
|
<property name="searchSortConfiguration">
|
||||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
<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">
|
<property name="sortFields">
|
||||||
<list>
|
<list>
|
||||||
|
<ref bean="sortScore" />
|
||||||
<ref bean="sortTitle"/>
|
<ref bean="sortTitle"/>
|
||||||
</list>
|
</list>
|
||||||
</property>
|
</property>
|
||||||
@@ -1107,11 +1087,9 @@
|
|||||||
<!--The sort filters for the discovery search-->
|
<!--The sort filters for the discovery search-->
|
||||||
<property name="searchSortConfiguration">
|
<property name="searchSortConfiguration">
|
||||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
<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">
|
<property name="sortFields">
|
||||||
<list>
|
<list>
|
||||||
|
<ref bean="sortScore" />
|
||||||
<ref bean="sortOrganizationLegalName"/>
|
<ref bean="sortOrganizationLegalName"/>
|
||||||
<ref bean="sortOrganizationAddressCountry"/>
|
<ref bean="sortOrganizationAddressCountry"/>
|
||||||
<ref bean="sortOrganizationAddressLocality"/>
|
<ref bean="sortOrganizationAddressLocality"/>
|
||||||
@@ -1169,11 +1147,9 @@
|
|||||||
<!--The sort filters for the discovery search-->
|
<!--The sort filters for the discovery search-->
|
||||||
<property name="searchSortConfiguration">
|
<property name="searchSortConfiguration">
|
||||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
<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">
|
<property name="sortFields">
|
||||||
<list>
|
<list>
|
||||||
|
<ref bean="sortScore" />
|
||||||
<ref bean="sortPublicationIssueNumber"/>
|
<ref bean="sortPublicationIssueNumber"/>
|
||||||
<ref bean="sortTitle"/>
|
<ref bean="sortTitle"/>
|
||||||
<ref bean="sortCreativeWorkDatePublished"/>
|
<ref bean="sortCreativeWorkDatePublished"/>
|
||||||
@@ -1229,11 +1205,9 @@
|
|||||||
<!--The sort filters for the discovery search-->
|
<!--The sort filters for the discovery search-->
|
||||||
<property name="searchSortConfiguration">
|
<property name="searchSortConfiguration">
|
||||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
<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">
|
<property name="sortFields">
|
||||||
<list>
|
<list>
|
||||||
|
<ref bean="sortScore" />
|
||||||
<ref bean="sortPublicationVolumeNumber"/>
|
<ref bean="sortPublicationVolumeNumber"/>
|
||||||
<ref bean="sortTitle"/>
|
<ref bean="sortTitle"/>
|
||||||
<ref bean="sortCreativeWorkDatePublished"/>
|
<ref bean="sortCreativeWorkDatePublished"/>
|
||||||
@@ -1290,11 +1264,9 @@
|
|||||||
<!--The sort filters for the discovery search-->
|
<!--The sort filters for the discovery search-->
|
||||||
<property name="searchSortConfiguration">
|
<property name="searchSortConfiguration">
|
||||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
<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">
|
<property name="sortFields">
|
||||||
<list>
|
<list>
|
||||||
|
<ref bean="sortScore" />
|
||||||
<ref bean="sortTitle"/>
|
<ref bean="sortTitle"/>
|
||||||
<ref bean="sortCreativeWorkDatePublished"/>
|
<ref bean="sortCreativeWorkDatePublished"/>
|
||||||
<ref bean="sortDateAccessioned"/>
|
<ref bean="sortDateAccessioned"/>
|
||||||
@@ -1360,9 +1332,6 @@
|
|||||||
<!--The sort filters for the discovery search-->
|
<!--The sort filters for the discovery search-->
|
||||||
<property name="searchSortConfiguration">
|
<property name="searchSortConfiguration">
|
||||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
<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">
|
<property name="sortFields">
|
||||||
<list>
|
<list>
|
||||||
<ref bean="sortEntityType"/>
|
<ref bean="sortEntityType"/>
|
||||||
@@ -1374,7 +1343,6 @@
|
|||||||
<ref bean="sortFamilyName"/>
|
<ref bean="sortFamilyName"/>
|
||||||
<ref bean="sortGivenName"/>
|
<ref bean="sortGivenName"/>
|
||||||
<ref bean="sortBirthDate"/>
|
<ref bean="sortBirthDate"/>
|
||||||
<ref bean="sortDateAccessioned"/>
|
|
||||||
</list>
|
</list>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
@@ -1422,11 +1390,9 @@
|
|||||||
<!--The sort filters for the discovery search-->
|
<!--The sort filters for the discovery search-->
|
||||||
<property name="searchSortConfiguration">
|
<property name="searchSortConfiguration">
|
||||||
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
|
<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">
|
<property name="sortFields">
|
||||||
<list>
|
<list>
|
||||||
|
<ref bean="sortScore" />
|
||||||
<ref bean="sortOrganizationLegalName"/>
|
<ref bean="sortOrganizationLegalName"/>
|
||||||
<ref bean="sortOrganizationAddressCountry"/>
|
<ref bean="sortOrganizationAddressCountry"/>
|
||||||
<ref bean="sortOrganizationAddressLocality"/>
|
<ref bean="sortOrganizationAddressLocality"/>
|
||||||
@@ -2180,59 +2146,77 @@
|
|||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<!--Sort properties-->
|
<!--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">
|
<bean id="sortTitle" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||||
<property name="metadataField" value="dc.title"/>
|
<property name="metadataField" value="dc.title"/>
|
||||||
|
<property name="defaultSortOrder" value="asc"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="sortDateIssued" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
<bean id="sortDateIssued" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||||
<property name="metadataField" value="dc.date.issued"/>
|
<property name="metadataField" value="dc.date.issued"/>
|
||||||
<property name="type" value="date"/>
|
<property name="type" value="date"/>
|
||||||
|
<property name="defaultSortOrder" value="desc"/>
|
||||||
</bean>
|
</bean>
|
||||||
<bean id="sortDateAccessioned" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
<bean id="sortDateAccessioned" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||||
<property name="metadataField" value="dc.date.accessioned"/>
|
<property name="metadataField" value="dc.date.accessioned"/>
|
||||||
<property name="type" value="date"/>
|
<property name="type" value="date"/>
|
||||||
|
<property name="defaultSortOrder" value="desc"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="sortFamilyName" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
<bean id="sortFamilyName" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||||
<property name="metadataField" value="person.familyName"/>
|
<property name="metadataField" value="person.familyName"/>
|
||||||
|
<property name="defaultSortOrder" value="asc"/>
|
||||||
</bean>
|
</bean>
|
||||||
<bean id="sortGivenName" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
<bean id="sortGivenName" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||||
<property name="metadataField" value="person.givenName"/>
|
<property name="metadataField" value="person.givenName"/>
|
||||||
|
<property name="defaultSortOrder" value="asc"/>
|
||||||
</bean>
|
</bean>
|
||||||
<bean id="sortBirthDate" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
<bean id="sortBirthDate" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||||
<property name="metadataField" value="person.birthDate"/>
|
<property name="metadataField" value="person.birthDate"/>
|
||||||
<property name="type" value="date"/>
|
<property name="type" value="date"/>
|
||||||
|
<property name="defaultSortOrder" value="desc"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="sortOrganizationLegalName" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
<bean id="sortOrganizationLegalName" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||||
<property name="metadataField" value="organization.legalName"/>
|
<property name="metadataField" value="organization.legalName"/>
|
||||||
|
<property name="defaultSortOrder" value="asc"/>
|
||||||
</bean>
|
</bean>
|
||||||
<bean id="sortOrganizationAddressCountry"
|
<bean id="sortOrganizationAddressCountry"
|
||||||
class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||||
<property name="metadataField" value="organisation.address.addressCountry"/>
|
<property name="metadataField" value="organisation.address.addressCountry"/>
|
||||||
|
<property name="defaultSortOrder" value="asc"/>
|
||||||
</bean>
|
</bean>
|
||||||
<bean id="sortOrganizationAddressLocality"
|
<bean id="sortOrganizationAddressLocality"
|
||||||
class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||||
<property name="metadataField" value="organisation.address.addressLocality"/>
|
<property name="metadataField" value="organisation.address.addressLocality"/>
|
||||||
|
<property name="defaultSortOrder" value="asc"/>
|
||||||
</bean>
|
</bean>
|
||||||
<bean id="sortOrganizationFoundingDate" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
<bean id="sortOrganizationFoundingDate" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||||
<property name="metadataField" value="organisation.foundingDate"/>
|
<property name="metadataField" value="organisation.foundingDate"/>
|
||||||
<property name="type" value="date"/>
|
<property name="type" value="date"/>
|
||||||
|
<property name="defaultSortOrder" value="desc"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="sortPublicationIssueNumber" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
<bean id="sortPublicationIssueNumber" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||||
<property name="metadataField" value="publicationissue.issueNumber"/>
|
<property name="metadataField" value="publicationissue.issueNumber"/>
|
||||||
|
<property name="defaultSortOrder" value="desc"/>
|
||||||
</bean>
|
</bean>
|
||||||
<bean id="sortCreativeWorkDatePublished" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
<bean id="sortCreativeWorkDatePublished" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||||
<property name="metadataField" value="creativework.datePublished"/>
|
<property name="metadataField" value="creativework.datePublished"/>
|
||||||
|
<property name="defaultSortOrder" value="desc"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="sortPublicationVolumeNumber" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
<bean id="sortPublicationVolumeNumber" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||||
<property name="metadataField" value="publicationvolume.volumeNumber"/>
|
<property name="metadataField" value="publicationvolume.volumeNumber"/>
|
||||||
|
<property name="defaultSortOrder" value="desc"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="sortEntityType" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
<bean id="sortEntityType" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
|
||||||
<property name="metadataField" value="dspace.entity.type"/>
|
<property name="metadataField" value="dspace.entity.type"/>
|
||||||
|
<property name="defaultSortOrder" value="desc"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
@@ -21,6 +21,7 @@
|
|||||||
<!-- serves as a 'this field is required' flag. -->
|
<!-- serves as a 'this field is required' flag. -->
|
||||||
|
|
||||||
<form-definitions>
|
<form-definitions>
|
||||||
|
<!-- Form used for entering in Bitstream/File metadata after uploading a file -->
|
||||||
<form name="bitstream-metadata">
|
<form name="bitstream-metadata">
|
||||||
<row>
|
<row>
|
||||||
<field>
|
<field>
|
||||||
@@ -47,7 +48,203 @@
|
|||||||
</row>
|
</row>
|
||||||
</form>
|
</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">
|
<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>
|
<row>
|
||||||
<relation-field>
|
<relation-field>
|
||||||
<relationship-type>isAuthorOfPublication</relationship-type>
|
<relationship-type>isAuthorOfPublication</relationship-type>
|
||||||
@@ -190,60 +387,8 @@
|
|||||||
</row>
|
</row>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form name="traditionalpagetwo">
|
<!-- Form which defines the metadata page/section for depositing Person Entities -->
|
||||||
<row>
|
<form name="personStep">
|
||||||
<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">
|
|
||||||
<row>
|
<row>
|
||||||
<relation-field>
|
<relation-field>
|
||||||
<relationship-type>isPublicationOfAuthor</relationship-type>
|
<relationship-type>isPublicationOfAuthor</relationship-type>
|
||||||
@@ -320,6 +465,7 @@
|
|||||||
</row>
|
</row>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<!-- Form which defines the metadata page/section for depositing Project Entities -->
|
||||||
<form name="projectStep">
|
<form name="projectStep">
|
||||||
<row>
|
<row>
|
||||||
<field>
|
<field>
|
||||||
@@ -390,6 +536,7 @@
|
|||||||
</row>
|
</row>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<!-- Form which defines the metadata page/section for depositing OrgUnit Entities -->
|
||||||
<form name="orgUnitStep">
|
<form name="orgUnitStep">
|
||||||
<row>
|
<row>
|
||||||
<field>
|
<field>
|
||||||
@@ -449,6 +596,7 @@
|
|||||||
</row>
|
</row>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<!-- Form which defines the metadata page/section for depositing Journal Entities -->
|
||||||
<form name="journalStep">
|
<form name="journalStep">
|
||||||
<row>
|
<row>
|
||||||
<field>
|
<field>
|
||||||
@@ -498,6 +646,7 @@
|
|||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<!-- Form which defines the metadata page/section for depositing JournalVolume Entities -->
|
||||||
<form name="journalVolumeStep">
|
<form name="journalVolumeStep">
|
||||||
<row>
|
<row>
|
||||||
<relation-field>
|
<relation-field>
|
||||||
@@ -548,6 +697,7 @@
|
|||||||
</row>
|
</row>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<!-- Form which defines the metadata page/section for depositing JournalIssue Entities -->
|
||||||
<form name="journalIssueStep">
|
<form name="journalIssueStep">
|
||||||
<row>
|
<row>
|
||||||
<relation-field>
|
<relation-field>
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>modules</artifactId>
|
<artifactId>modules</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<relativePath>..</relativePath>
|
<relativePath>..</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-parent</artifactId>
|
<artifactId>dspace-parent</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>modules</artifactId>
|
<artifactId>modules</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<relativePath>..</relativePath>
|
<relativePath>..</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@ just adding new jar in the classloader</description>
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>modules</artifactId>
|
<artifactId>modules</artifactId>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<relativePath>..</relativePath>
|
<relativePath>..</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-parent</artifactId>
|
<artifactId>dspace-parent</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@@ -11,7 +11,8 @@ version: "3.7"
|
|||||||
services:
|
services:
|
||||||
dspace-cli:
|
dspace-cli:
|
||||||
environment:
|
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:
|
entrypoint:
|
||||||
- /bin/bash
|
- /bin/bash
|
||||||
- '-c'
|
- '-c'
|
||||||
|
@@ -12,5 +12,33 @@ services:
|
|||||||
dspacedb:
|
dspacedb:
|
||||||
image: dspace/dspace-postgres-pgcrypto:loadsql
|
image: dspace/dspace-postgres-pgcrypto:loadsql
|
||||||
environment:
|
environment:
|
||||||
# Double underbars in env names will be replaced with periods for apache commons
|
# This SQL is available from https://github.com/DSpace-Labs/AIP-Files/releases/tag/demo-entities-data
|
||||||
- LOADSQL=https://www.dropbox.com/s/4ap1y6deseoc8ws/dspace7-entities-2019-11-28.sql?dl=1
|
- 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
|
dspace.dir=/dspace
|
||||||
db.url=jdbc:postgresql://dspacedb:5432/dspace
|
|
||||||
dspace.server.url=http://localhost:8080/server
|
dspace.server.url=http://localhost:8080/server
|
||||||
dspace.ui.url=http://localhost:4000
|
dspace.ui.url=http://localhost:4000
|
||||||
dspace.name=DSpace Started with Docker Compose
|
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
|
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>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-parent</artifactId>
|
<artifactId>dspace-parent</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<name>DSpace Parent Project</name>
|
<name>DSpace Parent Project</name>
|
||||||
<description>
|
<description>
|
||||||
DSpace open source software is a turnkey institutional repository application.
|
DSpace open source software is a turnkey institutional repository application.
|
||||||
@@ -30,12 +30,12 @@
|
|||||||
<axiom.version>1.2.22</axiom.version>
|
<axiom.version>1.2.22</axiom.version>
|
||||||
<errorprone.version>2.3.4</errorprone.version>
|
<errorprone.version>2.3.4</errorprone.version>
|
||||||
<!-- NOTE: when updating jackson.version, also sync jackson-databind dependency below -->
|
<!-- 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>
|
<javax-annotation.version>1.3.2</javax-annotation.version>
|
||||||
<jaxb-api.version>2.3.1</jaxb-api.version>
|
<jaxb-api.version>2.3.1</jaxb-api.version>
|
||||||
<jaxb-runtime.version>2.3.1</jaxb-runtime.version>
|
<jaxb-runtime.version>2.3.1</jaxb-runtime.version>
|
||||||
<!-- NOTE: Jetty needed for Solr, Handle Server & tests -->
|
<!-- 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>
|
<log4j.version>2.13.3</log4j.version>
|
||||||
<pdfbox-version>2.0.15</pdfbox-version>
|
<pdfbox-version>2.0.15</pdfbox-version>
|
||||||
<poi-version>3.17</poi-version>
|
<poi-version>3.17</poi-version>
|
||||||
@@ -826,14 +826,14 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-rest</artifactId>
|
<artifactId>dspace-rest</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<type>jar</type>
|
<type>jar</type>
|
||||||
<classifier>classes</classifier>
|
<classifier>classes</classifier>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-rest</artifactId>
|
<artifactId>dspace-rest</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<type>war</type>
|
<type>war</type>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
@@ -980,64 +980,64 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-api</artifactId>
|
<artifactId>dspace-api</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-api</artifactId>
|
<artifactId>dspace-api</artifactId>
|
||||||
<type>test-jar</type>
|
<type>test-jar</type>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dspace.modules</groupId>
|
<groupId>org.dspace.modules</groupId>
|
||||||
<artifactId>additions</artifactId>
|
<artifactId>additions</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-sword</artifactId>
|
<artifactId>dspace-sword</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-swordv2</artifactId>
|
<artifactId>dspace-swordv2</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-oai</artifactId>
|
<artifactId>dspace-oai</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-services</artifactId>
|
<artifactId>dspace-services</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-server-webapp</artifactId>
|
<artifactId>dspace-server-webapp</artifactId>
|
||||||
<type>test-jar</type>
|
<type>test-jar</type>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-rdf</artifactId>
|
<artifactId>dspace-rdf</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-server-webapp</artifactId>
|
<artifactId>dspace-server-webapp</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<type>jar</type>
|
<type>jar</type>
|
||||||
<classifier>classes</classifier>
|
<classifier>classes</classifier>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dspace</groupId>
|
<groupId>org.dspace</groupId>
|
||||||
<artifactId>dspace-server-webapp</artifactId>
|
<artifactId>dspace-server-webapp</artifactId>
|
||||||
<version>7.0-beta5-SNAPSHOT</version>
|
<version>7.0-beta6-SNAPSHOT</version>
|
||||||
<type>war</type>
|
<type>war</type>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- DSpace API Localization Packages -->
|
<!-- DSpace API Localization Packages -->
|
||||||
@@ -1369,7 +1369,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-io</groupId>
|
<groupId>commons-io</groupId>
|
||||||
<artifactId>commons-io</artifactId>
|
<artifactId>commons-io</artifactId>
|
||||||
<version>2.6</version>
|
<version>2.7</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
|
Reference in New Issue
Block a user