From 952c26b339589a7b0ab2c8e39daed4a824575c67 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Mon, 9 Sep 2024 14:19:03 +0200 Subject: [PATCH 1/6] [DSC-1905] Fixes authorizations for processes --- .../org/dspace/administer/ProcessCleaner.java | 23 +++++++- .../ProcessCleanerConfiguration.java | 16 ++++++ .../dspace/app/bulkedit/CollectionExport.java | 4 +- .../dspace/app/bulkedit/MetadataExport.java | 44 ++++++++++++--- .../MetadataExportScriptConfiguration.java | 15 ++++++ .../app/bulkedit/MetadataExportSearch.java | 54 ++++++++++++++----- ...tadataExportSearchScriptConfiguration.java | 16 ++++++ .../dspace/app/bulkedit/MetadataImport.java | 11 +++- .../MetadataImportScriptConfiguration.java | 15 ++++++ .../org/dspace/app/itemexport/ItemExport.java | 14 ++++- .../ItemExportScriptConfiguration.java | 16 ++++++ .../org/dspace/app/itemimport/ItemImport.java | 4 +- .../ItemImportScriptConfiguration.java | 15 ++++++ .../app/mediafilter/MediaFilterScript.java | 31 ++++++++--- .../MediaFilterScriptConfiguration.java | 16 ++++++ .../BulkItemExportScriptConfiguration.java | 8 ++- .../org/dspace/scripts/DSpaceRunnable.java | 40 ++++++++++++++ dspace/config/spring/rest/scripts.xml | 6 +++ 18 files changed, 307 insertions(+), 41 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/administer/ProcessCleaner.java b/dspace-api/src/main/java/org/dspace/administer/ProcessCleaner.java index ee6b8d08b05..2de41f5b575 100644 --- a/dspace-api/src/main/java/org/dspace/administer/ProcessCleaner.java +++ b/dspace-api/src/main/java/org/dspace/administer/ProcessCleaner.java @@ -12,12 +12,15 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.UUID; import org.apache.commons.cli.ParseException; import org.apache.commons.lang.time.DateUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.ProcessStatus; import org.dspace.core.Context; +import org.dspace.eperson.EPerson; +import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.scripts.DSpaceRunnable; import org.dspace.scripts.Process; import org.dspace.scripts.factory.ScriptServiceFactory; @@ -78,12 +81,14 @@ public void internalRun() throws Exception { } Context context = new Context(); + assignCurrentUserInContext(context); + assignSpecialGroupsInContext(context); try { - context.turnOffAuthorisationSystem(); + handleAuthorizationSystem(context); performDeletion(context); } finally { - context.restoreAuthSystemState(); + handleAuthorizationSystem(context); context.complete(); } @@ -137,4 +142,18 @@ public ProcessCleanerConfiguration getScriptConfiguration() { .getServiceByName("process-cleaner", ProcessCleanerConfiguration.class); } + private void assignCurrentUserInContext(Context context) throws SQLException { + UUID uuid = getEpersonIdentifier(); + if (uuid != null) { + EPerson ePerson = EPersonServiceFactory.getInstance().getEPersonService().find(context, uuid); + context.setCurrentUser(ePerson); + } + } + + private void assignSpecialGroupsInContext(Context context) throws SQLException { + for (UUID uuid : handler.getSpecialGroups()) { + context.setSpecialGroup(uuid); + } + } + } diff --git a/dspace-api/src/main/java/org/dspace/administer/ProcessCleanerConfiguration.java b/dspace-api/src/main/java/org/dspace/administer/ProcessCleanerConfiguration.java index 91dcfb5dfec..e765c2a4c18 100644 --- a/dspace-api/src/main/java/org/dspace/administer/ProcessCleanerConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/administer/ProcessCleanerConfiguration.java @@ -7,7 +7,12 @@ */ package org.dspace.administer; +import java.sql.SQLException; +import java.util.List; + import org.apache.commons.cli.Options; +import org.dspace.core.Context; +import org.dspace.scripts.DSpaceCommandLineParameter; import org.dspace.scripts.configuration.ScriptConfiguration; /** @@ -17,6 +22,17 @@ public class ProcessCleanerConfiguration extends Scrip private Class dspaceRunnableClass; + @Override + public boolean isAllowedToExecute(Context context, List commandLineParameters) { + try { + return authorizeService.isAdmin(context) || authorizeService.isComColAdmin(context) || + authorizeService.isItemAdmin(context); + } catch (SQLException e) { + throw new RuntimeException( + "SQLException occurred when checking if the current user is eligible to run the script", e); + } + } + @Override public Options getOptions() { if (options == null) { diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/CollectionExport.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/CollectionExport.java index c95defee29c..b3fee2d40ca 100644 --- a/dspace-api/src/main/java/org/dspace/app/bulkedit/CollectionExport.java +++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/CollectionExport.java @@ -62,7 +62,7 @@ public void internalRun() throws Exception { assignCurrentUserInContext(); assignSpecialGroupsInContext(); - context.turnOffAuthorisationSystem(); + handleAuthorizationSystem(context); Collection collection = getCollection(); if (collection == null) { @@ -76,7 +76,7 @@ public void internalRun() throws Exception { try { performExport(collection); context.complete(); - context.restoreAuthSystemState(); + handleAuthorizationSystem(context); } catch (Exception e) { handler.handleException(e); context.abort(); diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExport.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExport.java index 739e7a648e6..49a76a6bedd 100644 --- a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExport.java +++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExport.java @@ -7,6 +7,7 @@ */ package org.dspace.app.bulkedit; +import java.io.InputStream; import java.sql.SQLException; import java.util.UUID; @@ -18,6 +19,7 @@ import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.MetadataDSpaceCsvExportService; import org.dspace.core.Context; +import org.dspace.eperson.EPerson; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.EPersonService; import org.dspace.handle.factory.HandleServiceFactory; @@ -54,19 +56,31 @@ public void internalRun() throws Exception { printHelp(); return; } + Context context = new Context(); - context.turnOffAuthorisationSystem(); try { - context.setCurrentUser(ePersonService.find(context, this.getEpersonIdentifier())); + assignCurrentUserInContext(context); + assignSpecialGroupsInContext(context); } catch (SQLException e) { handler.handleException(e); } - DSpaceCSV dSpaceCSV = metadataDSpaceCsvExportService - .handleExport(context, exportAllItems, exportAllMetadata, identifier, - handler); - handler.writeFilestream(context, filename, dSpaceCSV.getInputStream(), EXPORT_CSV); - context.restoreAuthSystemState(); - context.complete(); + + handleAuthorizationSystem(context); + try { + DSpaceCSV dSpaceCSV = + metadataDSpaceCsvExportService.handleExport( + context, exportAllItems, exportAllMetadata, identifier, handler + ); + try (InputStream is = dSpaceCSV.getInputStream()) { + handler.writeFilestream(context, filename, is, EXPORT_CSV); + } + + handleAuthorizationSystem(context); + context.complete(); + } catch (Exception e) { + handler.handleException(e); + context.abort(); + } } protected void logHelpInfo() { @@ -119,4 +133,18 @@ protected String getFileNameForExportFile() throws ParseException { } return null; } + + private void assignCurrentUserInContext(Context context) throws SQLException { + UUID uuid = getEpersonIdentifier(); + if (uuid != null) { + EPerson ePerson = EPersonServiceFactory.getInstance().getEPersonService().find(context, uuid); + context.setCurrentUser(ePerson); + } + } + + private void assignSpecialGroupsInContext(Context context) throws SQLException { + for (UUID uuid : handler.getSpecialGroups()) { + context.setSpecialGroup(uuid); + } + } } diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportScriptConfiguration.java index aa76c09c0a5..707098f7587 100644 --- a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportScriptConfiguration.java @@ -7,7 +7,12 @@ */ package org.dspace.app.bulkedit; +import java.sql.SQLException; +import java.util.List; + import org.apache.commons.cli.Options; +import org.dspace.core.Context; +import org.dspace.scripts.DSpaceCommandLineParameter; import org.dspace.scripts.configuration.ScriptConfiguration; /** @@ -17,6 +22,16 @@ public class MetadataExportScriptConfiguration extends private Class dspaceRunnableClass; + public boolean isAllowedToExecute(Context context, List commandLineParameters) { + try { + return authorizeService.isAdmin(context) || authorizeService.isComColAdmin(context) || + authorizeService.isItemAdmin(context); + } catch (SQLException e) { + throw new RuntimeException( + "SQLException occurred when checking if the current user is eligible to run the script", e); + } + } + @Override public Class getDspaceRunnableClass() { return dspaceRunnableClass; diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportSearch.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportSearch.java index 4f2c92d27e6..0105c516b0a 100644 --- a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportSearch.java +++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportSearch.java @@ -8,6 +8,7 @@ package org.dspace.app.bulkedit; +import java.io.InputStream; import java.sql.SQLException; import java.util.ArrayList; import java.util.Iterator; @@ -32,6 +33,7 @@ import org.dspace.discovery.indexobject.IndexableCommunity; import org.dspace.discovery.utils.DiscoverQueryBuilder; import org.dspace.discovery.utils.parameter.QueryBuilderSearchFilter; +import org.dspace.eperson.EPerson; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.EPersonService; import org.dspace.scripts.DSpaceRunnable; @@ -113,7 +115,8 @@ public void internalRun() throws Exception { IndexableObject dso = null; Context context = new Context(); - context.setCurrentUser(ePersonService.find(context, this.getEpersonIdentifier())); + assignCurrentUserInContext(context); + assignSpecialGroupsInContext(context); if (hasScope) { dso = resolveScope(context, identifier); @@ -135,20 +138,28 @@ public void internalRun() throws Exception { queryBuilderSearchFilters.add(queryBuilderSearchFilter); } } - handler.logDebug("building query"); - DiscoverQuery discoverQuery = - queryBuilder.buildQuery(context, dso, discoveryConfiguration, query, queryBuilderSearchFilters, - "Item", 10, Long.getLong("0"), null, SortOption.DESCENDING); - handler.logDebug("creating iterator"); - - Iterator itemIterator = searchService.iteratorSearch(context, dso, discoverQuery); - handler.logDebug("creating dspacecsv"); - DSpaceCSV dSpaceCSV = metadataDSpaceCsvExportService.export(context, itemIterator, true); - handler.logDebug("writing to file " + getFileNameOrExportFile()); - handler.writeFilestream(context, getFileNameOrExportFile(), dSpaceCSV.getInputStream(), EXPORT_CSV); - context.restoreAuthSystemState(); - context.complete(); + try { + handler.logDebug("building query"); + DiscoverQuery discoverQuery = + queryBuilder.buildQuery(context, dso, discoveryConfiguration, query, queryBuilderSearchFilters, + "Item", 10, Long.getLong("0"), null, SortOption.DESCENDING); + + handler.logDebug("creating iterator"); + Iterator itemIterator = searchService.iteratorSearch(context, dso, discoverQuery); + handler.logDebug("creating dspacecsv"); + DSpaceCSV dSpaceCSV = metadataDSpaceCsvExportService.export(context, itemIterator, true); + + try (InputStream is = dSpaceCSV.getInputStream()) { + handler.logDebug("writing to file " + getFileNameOrExportFile()); + handler.writeFilestream(context, getFileNameOrExportFile(), is, EXPORT_CSV); + } + handleAuthorizationSystem(context); + context.complete(); + } catch (Exception e) { + handler.handleException(e); + context.abort(); + } } protected void loghelpinfo() { @@ -159,6 +170,21 @@ protected String getFileNameOrExportFile() { return "metadataExportSearch.csv"; } + private void assignCurrentUserInContext(Context context) throws SQLException { + UUID uuid = getEpersonIdentifier(); + if (uuid != null) { + EPerson ePerson = EPersonServiceFactory.getInstance().getEPersonService().find(context, uuid); + context.setCurrentUser(ePerson); + } + } + + private void assignSpecialGroupsInContext(Context context) throws SQLException { + for (UUID uuid : handler.getSpecialGroups()) { + context.setSpecialGroup(uuid); + } + } + + public IndexableObject resolveScope(Context context, String id) throws SQLException { UUID uuid = UUID.fromString(id); IndexableObject scopeObj = new IndexableCommunity(communityService.find(context, uuid)); diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportSearchScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportSearchScriptConfiguration.java index 4f2a225d3ac..07bdcb14763 100644 --- a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportSearchScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportSearchScriptConfiguration.java @@ -8,7 +8,12 @@ package org.dspace.app.bulkedit; +import java.sql.SQLException; +import java.util.List; + import org.apache.commons.cli.Options; +import org.dspace.core.Context; +import org.dspace.scripts.DSpaceCommandLineParameter; import org.dspace.scripts.configuration.ScriptConfiguration; /** @@ -18,6 +23,17 @@ public class MetadataExportSearchScriptConfiguration dspaceRunnableclass; + @Override + public boolean isAllowedToExecute(Context context, List commandLineParameters) { + try { + return authorizeService.isAdmin(context) || authorizeService.isComColAdmin(context) || + authorizeService.isItemAdmin(context); + } catch (SQLException e) { + throw new RuntimeException( + "SQLException occurred when checking if the current user is eligible to run the script", e); + } + } + @Override public Class getDspaceRunnableClass() { return dspaceRunnableclass; diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImport.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImport.java index 5a37d567659..503fb90df1d 100644 --- a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImport.java +++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImport.java @@ -182,10 +182,10 @@ public void internalRun() throws Exception { // Create a context Context c = null; c = new Context(); - c.turnOffAuthorisationSystem(); // Find the EPerson, assign to context assignCurrentUserInContext(c); + assignSpecialGroupsInContext(c); // Read commandLines from the CSV file try { @@ -207,6 +207,7 @@ public void internalRun() throws Exception { initMetadataImport(csv); List changes; + handleAuthorizationSystem(c); if (!commandLine.hasOption('s') || validateOnly) { // See what has changed try { @@ -250,7 +251,7 @@ public void internalRun() throws Exception { } // Finsh off and tidy up - c.restoreAuthSystemState(); + handleAuthorizationSystem(c); c.complete(); } catch (Exception e) { c.abort(); @@ -272,6 +273,12 @@ protected void assignCurrentUserInContext(Context context) throws ParseException } } + private void assignSpecialGroupsInContext(Context context) throws SQLException { + for (UUID uuid : handler.getSpecialGroups()) { + context.setSpecialGroup(uuid); + } + } + /** * This method determines whether the changes should be applied or not. This is default set to true for the REST * script as we don't want to interact with the caller. This will be overwritten in the CLI script to ask for diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImportScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImportScriptConfiguration.java index ce2f7fb68af..fb32e6ab2c1 100644 --- a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImportScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImportScriptConfiguration.java @@ -8,8 +8,12 @@ package org.dspace.app.bulkedit; import java.io.InputStream; +import java.sql.SQLException; +import java.util.List; import org.apache.commons.cli.Options; +import org.dspace.core.Context; +import org.dspace.scripts.DSpaceCommandLineParameter; import org.dspace.scripts.configuration.ScriptConfiguration; /** @@ -19,6 +23,17 @@ public class MetadataImportScriptConfiguration extends private Class dspaceRunnableClass; + @Override + public boolean isAllowedToExecute(Context context, List commandLineParameters) { + try { + return authorizeService.isAdmin(context) || authorizeService.isComColAdmin(context) || + authorizeService.isItemAdmin(context); + } catch (SQLException e) { + throw new RuntimeException( + "SQLException occurred when checking if the current user is eligible to run the script", e); + } + } + @Override public Class getDspaceRunnableClass() { return dspaceRunnableClass; diff --git a/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExport.java b/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExport.java index 71fc088694d..309f61678bb 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExport.java +++ b/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExport.java @@ -128,7 +128,8 @@ public void internalRun() throws Exception { validate(); Context context = new Context(); - context.turnOffAuthorisationSystem(); + setEPerson(context); + assignSpecialGroupsInContext(context); if (type == Constants.ITEM) { // first, is myIDString a handle? @@ -171,8 +172,12 @@ public void internalRun() throws Exception { .getItemExportService(); try { itemExportService.setHandler(handler); + handleAuthorizationSystem(context); + process(context, itemExportService); + context.complete(); + handleAuthorizationSystem(context); } catch (Exception e) { context.abort(); throw new Exception(e); @@ -200,7 +205,6 @@ protected void validate() { * @throws Exception */ protected void process(Context context, ItemExportService itemExportService) throws Exception { - setEPerson(context); setDestDirName(context, itemExportService); setZip(context); @@ -261,4 +265,10 @@ private void setEPerson(Context context) throws SQLException { context.setCurrentUser(myEPerson); } + + private void assignSpecialGroupsInContext(Context context) throws SQLException { + for (UUID uuid : handler.getSpecialGroups()) { + context.setSpecialGroup(uuid); + } + } } diff --git a/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportScriptConfiguration.java index b37df5f5ea5..1f0c98c44ca 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportScriptConfiguration.java @@ -7,8 +7,13 @@ */ package org.dspace.app.itemexport; +import java.sql.SQLException; +import java.util.List; + import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; +import org.dspace.core.Context; +import org.dspace.scripts.DSpaceCommandLineParameter; import org.dspace.scripts.configuration.ScriptConfiguration; /** @@ -20,6 +25,17 @@ public class ItemExportScriptConfiguration extends ScriptC private Class dspaceRunnableClass; + @Override + public boolean isAllowedToExecute(Context context, List commandLineParameters) { + try { + return authorizeService.isAdmin(context) || authorizeService.isComColAdmin(context) || + authorizeService.isItemAdmin(context); + } catch (SQLException e) { + throw new RuntimeException( + "SQLException occurred when checking if the current user is eligible to run the script", e); + } + } + @Override public Class getDspaceRunnableClass() { return dspaceRunnableClass; diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImport.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImport.java index b32de11f7a7..829b88021f0 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImport.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImport.java @@ -219,7 +219,7 @@ public void internalRun() throws Exception { itemImportService.setHandler(handler); try { - context.turnOffAuthorisationSystem(); + handleAuthorizationSystem(context); readZip(context, itemImportService); @@ -231,6 +231,8 @@ public void internalRun() throws Exception { context.abort(); throw new Exception( "Error committing changes to database: " + e.getMessage() + ", aborting most recent changes", e); + } finally { + handleAuthorizationSystem(context); } if (isTest) { diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportScriptConfiguration.java index 3f2675ea58f..ca83ce9c3c6 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportScriptConfiguration.java @@ -8,9 +8,13 @@ package org.dspace.app.itemimport; import java.io.InputStream; +import java.sql.SQLException; +import java.util.List; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; +import org.dspace.core.Context; +import org.dspace.scripts.DSpaceCommandLineParameter; import org.dspace.scripts.configuration.ScriptConfiguration; /** @@ -22,6 +26,17 @@ public class ItemImportScriptConfiguration extends ScriptC private Class dspaceRunnableClass; + @Override + public boolean isAllowedToExecute(Context context, List commandLineParameters) { + try { + return authorizeService.isAdmin(context) || authorizeService.isComColAdmin(context) || + authorizeService.isItemAdmin(context); + } catch (SQLException e) { + throw new RuntimeException( + "SQLException occurred when checking if the current user is eligible to run the script", e); + } + } + @Override public Class getDspaceRunnableClass() { return dspaceRunnableClass; diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java index 5fbbebbb28c..593ff7a96eb 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java @@ -7,12 +7,14 @@ */ package org.dspace.app.mediafilter; +import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.UUID; import org.apache.commons.cli.ParseException; import org.apache.commons.lang3.ArrayUtils; @@ -26,6 +28,8 @@ import org.dspace.core.Context; import org.dspace.core.SelfNamedPlugin; import org.dspace.core.factory.CoreServiceFactory; +import org.dspace.eperson.EPerson; +import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.scripts.DSpaceRunnable; import org.dspace.services.factory.DSpaceServicesFactory; @@ -216,12 +220,12 @@ public void internalRun() throws Exception { } Context c = null; - try { c = new Context(); + assignCurrentUserInContext(c); + assignSpecialGroupsInContext(c); - // have to be super-user to do the filtering - c.turnOffAuthorisationSystem(); + handleAuthorizationSystem(c); // now apply the filters if (identifier == null) { @@ -249,14 +253,25 @@ public void internalRun() throws Exception { } } + handleAuthorizationSystem(c); c.complete(); - c = null; } catch (Exception e) { handler.handleException(e); - } finally { - if (c != null) { - c.abort(); - } + c.abort(); + } + } + + private void assignCurrentUserInContext(Context context) throws SQLException { + UUID uuid = getEpersonIdentifier(); + if (uuid != null) { + EPerson ePerson = EPersonServiceFactory.getInstance().getEPersonService().find(context, uuid); + context.setCurrentUser(ePerson); + } + } + + private void assignSpecialGroupsInContext(Context context) throws SQLException { + for (UUID uuid : handler.getSpecialGroups()) { + context.setSpecialGroup(uuid); } } } diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScriptConfiguration.java index 7465fa6e127..38010b93104 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScriptConfiguration.java @@ -7,8 +7,13 @@ */ package org.dspace.app.mediafilter; +import java.sql.SQLException; +import java.util.List; + import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; +import org.dspace.core.Context; +import org.dspace.scripts.DSpaceCommandLineParameter; import org.dspace.scripts.configuration.ScriptConfiguration; public class MediaFilterScriptConfiguration extends ScriptConfiguration { @@ -17,6 +22,17 @@ public class MediaFilterScriptConfiguration extends private static final String MEDIA_FILTER_PLUGINS_KEY = "filter.plugins"; + @Override + public boolean isAllowedToExecute(Context context, List commandLineParameters) { + try { + return authorizeService.isAdmin(context) || authorizeService.isComColAdmin(context) || + authorizeService.isItemAdmin(context); + } catch (SQLException e) { + throw new RuntimeException( + "SQLException occurred when checking if the current user is eligible to run the script", e); + } + } + @Override public Class getDspaceRunnableClass() { return dspaceRunnableClass; diff --git a/dspace-api/src/main/java/org/dspace/content/integration/crosswalks/script/BulkItemExportScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/content/integration/crosswalks/script/BulkItemExportScriptConfiguration.java index def5016b7d7..ee0a1712397 100644 --- a/dspace-api/src/main/java/org/dspace/content/integration/crosswalks/script/BulkItemExportScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/content/integration/crosswalks/script/BulkItemExportScriptConfiguration.java @@ -37,11 +37,15 @@ public boolean isAllowedToExecute(Context context, List "loggedIn") - .orElse("notLoggedIn")); + .orElse("notLoggedIn")); } } catch (SQLException e) { return false; diff --git a/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java b/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java index 5f9693fee69..3d04d6116e4 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java +++ b/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java @@ -8,6 +8,7 @@ package org.dspace.scripts; import java.io.InputStream; +import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -18,7 +19,9 @@ import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.lang3.StringUtils; +import org.dspace.authorize.factory.AuthorizeServiceFactory; import org.dspace.cli.DSpaceSkipUnknownArgumentsParser; +import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.dspace.scripts.configuration.ScriptConfiguration; import org.dspace.scripts.handler.DSpaceRunnableHandler; @@ -53,6 +56,11 @@ public abstract class DSpaceRunnable implements R */ protected DSpaceRunnableHandler handler; + /** + * Tells if the current context user that is running the script is an admin + */ + private Boolean isAdmin = null; + /** * This method will return the Configuration that the implementing DSpaceRunnable uses * @return The {@link ScriptConfiguration} that this implementing DspaceRunnable uses @@ -212,4 +220,36 @@ public void setEpersonIdentifier(UUID epersonIdentifier) { public enum StepResult { Continue, Exit; } + + protected boolean isAdmin() { + return isAdmin; + } + + protected void setAdmin(boolean admin) { + isAdmin = admin; + } + + /** + * Method that handles the authorization system for the script. + * It will disable the authorization system if the script is run as an admin. + * It will restore authorization system if is in ignore state and if the script is run as an admin. + * + * @param context current DSpace Context + */ + protected void handleAuthorizationSystem(Context context) { + if (this.isAdmin == null) { + try { + this.setAdmin(AuthorizeServiceFactory.getInstance().getAuthorizeService().isAdmin(context)); + } catch (SQLException e) { + handler.handleException("Cannot determine if the script is run as an admin", e); + } + } + if (this.isAdmin) { + if (context.ignoreAuthorization()) { + context.restoreAuthSystemState(); + } else { + context.turnOffAuthorisationSystem(); + } + } + } } diff --git a/dspace/config/spring/rest/scripts.xml b/dspace/config/spring/rest/scripts.xml index 343368fc919..8e4d6393f21 100644 --- a/dspace/config/spring/rest/scripts.xml +++ b/dspace/config/spring/rest/scripts.xml @@ -118,4 +118,10 @@ + + + + + + From 9fccb9004bc3d418ed13bb12cc2feb8971493b42 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Mon, 9 Sep 2024 18:05:42 +0200 Subject: [PATCH 2/6] [DSC-1905] Fixes Scripts ITs failures --- .../org/dspace/app/itemexport/ItemExport.java | 6 ++--- .../dspace/app/itemexport/ItemExportCLI.java | 9 +++++++ .../app/mediafilter/MediaFilterCliScript.java | 25 +++++++++++++++++++ .../MediaFilterCliScriptConfiguration.java | 15 +++++++++++ .../app/mediafilter/MediaFilterScript.java | 2 +- .../crosswalks/script/ItemExport.java | 6 ++--- .../config/spring/api/scripts.xml | 4 +-- .../dspace/app/bulkedit/MetadataImportIT.java | 9 ++++--- .../app/csv/CSVMetadataImportReferenceIT.java | 4 +-- dspace/config/spring/api/scripts.xml | 4 +-- 10 files changed, 67 insertions(+), 17 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterCliScript.java create mode 100644 dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterCliScriptConfiguration.java diff --git a/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExport.java b/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExport.java index 309f61678bb..6267dc46e20 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExport.java +++ b/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExport.java @@ -128,8 +128,6 @@ public void internalRun() throws Exception { validate(); Context context = new Context(); - setEPerson(context); - assignSpecialGroupsInContext(context); if (type == Constants.ITEM) { // first, is myIDString a handle? @@ -205,6 +203,8 @@ protected void validate() { * @throws Exception */ protected void process(Context context, ItemExportService itemExportService) throws Exception { + setEPerson(context); + assignSpecialGroupsInContext(context); setDestDirName(context, itemExportService); setZip(context); @@ -254,7 +254,7 @@ protected void setNumber() { } } - private void setEPerson(Context context) throws SQLException { + protected void setEPerson(Context context) throws SQLException { EPerson myEPerson = epersonService.find(context, this.getEpersonIdentifier()); // check eperson diff --git a/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportCLI.java b/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportCLI.java index 8e9af1e0109..fae55cc2d43 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportCLI.java +++ b/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportCLI.java @@ -93,4 +93,13 @@ protected void setNumber() { seqStart = Integer.parseInt(commandLine.getOptionValue('n')); } } + + @Override + protected void handleAuthorizationSystem(Context context) { + if (context.ignoreAuthorization()) { + context.restoreAuthSystemState(); + } else { + context.turnOffAuthorisationSystem(); + } + } } diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterCliScript.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterCliScript.java new file mode 100644 index 00000000000..6f28c9a9e22 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterCliScript.java @@ -0,0 +1,25 @@ +/** + * 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.mediafilter; + +import org.dspace.core.Context; + +/** + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public class MediaFilterCliScript extends MediaFilterScript { + + @Override + protected void handleAuthorizationSystem(Context context) { + if (context.ignoreAuthorization()) { + context.restoreAuthSystemState(); + } else { + context.turnOffAuthorisationSystem(); + } + } +} diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterCliScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterCliScriptConfiguration.java new file mode 100644 index 00000000000..bf5f1750d6c --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterCliScriptConfiguration.java @@ -0,0 +1,15 @@ +/** + * 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.mediafilter; + +/** + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public class MediaFilterCliScriptConfiguration extends MediaFilterScriptConfiguration { + +} diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java index 593ff7a96eb..e91b46d7db9 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java @@ -261,7 +261,7 @@ public void internalRun() throws Exception { } } - private void assignCurrentUserInContext(Context context) throws SQLException { + protected void assignCurrentUserInContext(Context context) throws SQLException { UUID uuid = getEpersonIdentifier(); if (uuid != null) { EPerson ePerson = EPersonServiceFactory.getInstance().getEPersonService().find(context, uuid); diff --git a/dspace-api/src/main/java/org/dspace/content/integration/crosswalks/script/ItemExport.java b/dspace-api/src/main/java/org/dspace/content/integration/crosswalks/script/ItemExport.java index 43b5d0b0971..e99e33f7cdf 100644 --- a/dspace-api/src/main/java/org/dspace/content/integration/crosswalks/script/ItemExport.java +++ b/dspace-api/src/main/java/org/dspace/content/integration/crosswalks/script/ItemExport.java @@ -45,7 +45,7 @@ public class ItemExport extends DSpaceRunnable - + - + diff --git a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java index 79cf6f56d56..63af96adb7c 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java @@ -38,6 +38,7 @@ import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.ItemService; import org.dspace.content.service.RelationshipService; +import org.dspace.eperson.EPerson; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.EPersonService; import org.dspace.scripts.DSpaceRunnable; @@ -97,7 +98,7 @@ public void metadataImportTest() throws Exception { StringUtils.equals( itemService.getMetadata(importedItem, "dc", "contributor", "author", Item.ANY).get(0).getValue(), "Donald, SmithImported")); - eperson = ePersonService.findByEmail(context, eperson.getEmail()); + eperson = ePersonService.findByEmail(context, admin.getEmail()); assertEquals(importedItem.getSubmitter(), eperson); context.turnOffAuthorisationSystem(); @@ -115,7 +116,7 @@ public void metadataImportIntoCollectionWithEntityTypeWithTemplateEnabledTest() .get(0).getValue(), "Donald, SmithImported")); assertTrue(StringUtils.equals(itemService.getMetadata(importedItem, "dspace", "entity", "type", Item.ANY) .get(0).getValue(), "Publication")); - eperson = ePersonService.findByEmail(context, eperson.getEmail()); + EPerson eperson = ePersonService.findByEmail(context, admin.getEmail()); assertEquals(importedItem.getSubmitter(), eperson); context.turnOffAuthorisationSystem(); @@ -133,7 +134,7 @@ public void metadataImportIntoCollectionWithEntityTypeWithTemplateDisabledTest() .get(0).getValue(), "Donald, SmithImported")); assertEquals(1, itemService.getMetadata(importedItem, "dspace", "entity", "type", Item.ANY) .size()); - eperson = ePersonService.findByEmail(context, eperson.getEmail()); + EPerson eperson = ePersonService.findByEmail(context, admin.getEmail()); assertEquals(importedItem.getSubmitter(), eperson); context.turnOffAuthorisationSystem(); @@ -278,7 +279,7 @@ public void performImportScript(String[] csv, boolean useTemplate) throws Except out.close(); String fileLocation = csvFile.getAbsolutePath(); try { - String[] args = new String[] {"metadata-import", "-f", fileLocation, "-e", eperson.getEmail(), "-s"}; + String[] args = new String[] {"metadata-import", "-f", fileLocation, "-e", admin.getEmail(), "-s"}; if (useTemplate) { args = ArrayUtils.add(args, "-t"); } diff --git a/dspace-api/src/test/java/org/dspace/app/csv/CSVMetadataImportReferenceIT.java b/dspace-api/src/test/java/org/dspace/app/csv/CSVMetadataImportReferenceIT.java index 807a2e711e1..ff2d7afccf0 100644 --- a/dspace-api/src/test/java/org/dspace/app/csv/CSVMetadataImportReferenceIT.java +++ b/dspace-api/src/test/java/org/dspace/app/csv/CSVMetadataImportReferenceIT.java @@ -700,9 +700,9 @@ public int performImportScript(String[] csv, boolean validateOnly) throws Except try { String[] args = null; if (validateOnly) { - args = new String[] {"metadata-import", "-f", fileLocation, "-e", eperson.getEmail(), "-s", "-v"}; + args = new String[] {"metadata-import", "-f", fileLocation, "-e", admin.getEmail(), "-s", "-v"}; } else { - args = new String[] {"metadata-import", "-f", fileLocation, "-e", eperson.getEmail(), "-s",}; + args = new String[] {"metadata-import", "-f", fileLocation, "-e", admin.getEmail(), "-s",}; } TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler(); diff --git a/dspace/config/spring/api/scripts.xml b/dspace/config/spring/api/scripts.xml index a7851655c5a..2e775dbef10 100644 --- a/dspace/config/spring/api/scripts.xml +++ b/dspace/config/spring/api/scripts.xml @@ -135,9 +135,9 @@ - + - + From eed214bc0c2956ab8ab0758d95820f4602a85bce Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Mon, 9 Sep 2024 18:35:42 +0200 Subject: [PATCH 3/6] [DSC-1905] Fixes NPE --- dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java b/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java index 3d04d6116e4..cef1a3fd21b 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java +++ b/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java @@ -244,7 +244,7 @@ protected void handleAuthorizationSystem(Context context) { handler.handleException("Cannot determine if the script is run as an admin", e); } } - if (this.isAdmin) { + if (Boolean.TRUE.equals(this.isAdmin)) { if (context.ignoreAuthorization()) { context.restoreAuthSystemState(); } else { From 908e01084e01437d43d647e9bdfb303c29e47ba4 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Tue, 10 Sep 2024 10:29:07 +0200 Subject: [PATCH 4/6] [DSC-1905] Fixes ITs failures due to missing session to load lazy handle --- .../src/main/java/org/dspace/app/itemexport/ItemExport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExport.java b/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExport.java index 6267dc46e20..3c201eb571b 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExport.java +++ b/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExport.java @@ -211,7 +211,7 @@ protected void process(Context context, ItemExportService itemExportService) thr Iterator items; if (item != null) { List myItems = new ArrayList<>(); - myItems.add(item); + myItems.add(context.reloadEntity(item)); items = myItems.iterator(); } else { handler.logInfo("Exporting from collection: " + idString); From 7a373f6c5f098fef952b16e4bd8af3b3be3e44af Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Tue, 10 Sep 2024 13:03:36 +0200 Subject: [PATCH 5/6] [DSC-1905] Adds ITs for collection administrators --- .../org/dspace/app/bulkedit/BulkImportIT.java | 181 +++++++++++++++++ .../dspace/app/bulkedit/MetadataImportIT.java | 186 ++++++++++++++++++ .../dspace/app/itemimport/ItemImportIT.java | 62 ++++-- .../org/dspace/curate/CurationScriptIT.java | 55 ++++++ 4 files changed, 470 insertions(+), 14 deletions(-) create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/bulkedit/BulkImportIT.java create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/bulkedit/BulkImportIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/bulkedit/BulkImportIT.java new file mode 100644 index 00000000000..c63e71f41f1 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/bulkedit/BulkImportIT.java @@ -0,0 +1,181 @@ +/** + * 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.bulkedit; + +import static com.jayway.jsonpath.JsonPath.read; +import static org.dspace.app.matcher.MetadataValueMatcher.with; +import static org.dspace.builder.CollectionBuilder.createCollection; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItems; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.io.File; +import java.io.FileInputStream; +import java.sql.SQLException; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.dspace.app.rest.converter.DSpaceRunnableParameterConverter; +import org.dspace.app.rest.matcher.ProcessMatcher; +import org.dspace.app.rest.model.ParameterValueRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.app.rest.test.AbstractEntityIntegrationTest; +import org.dspace.authorize.AuthorizeException; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.ProcessBuilder; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.Item; +import org.dspace.content.MetadataValue; +import org.dspace.content.ProcessStatus; +import org.dspace.content.Relationship; +import org.dspace.content.WorkspaceItem; +import org.dspace.content.service.RelationshipService; +import org.dspace.content.service.WorkspaceItemService; +import org.dspace.eperson.EPerson; +import org.dspace.scripts.DSpaceCommandLineParameter; +import org.dspace.scripts.service.ProcessService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; + +/** + * Integration tests for BulkImportScript run through the ScriptRestRepository + * + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public class BulkImportIT extends AbstractEntityIntegrationTest { + + private static final String BASE_XLS_DIR_PATH = "./target/testing/dspace/assetstore/bulk-import/"; + + @Autowired + private RelationshipService relationshipService; + @Autowired + private ProcessService processService; + @Autowired + private WorkspaceItemService workspaceItemService; + @Autowired + private DSpaceRunnableParameterConverter dSpaceRunnableParameterConverter; + + private Community community; + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + context.turnOffAuthorisationSystem(); + community = CommunityBuilder.createCommunity(context).build(); + context.restoreAuthSystemState(); + } + + @After + public void after() throws SQLException, AuthorizeException { + context.turnOffAuthorisationSystem(); + List relationships = relationshipService.findAll(context); + for (Relationship relationship : relationships) { + relationshipService.delete(context, relationship); + } + context.restoreAuthSystemState(); + } + + @Test + public void testCreatePatent() throws Exception { + + context.turnOffAuthorisationSystem(); + Collection patents = createCollection(context, community) + .withSubmissionDefinition("patent") + .withAdminGroup(eperson) + .build(); + context.commit(); + context.restoreAuthSystemState(); + + File xlsFile = getXlsFile("create-patent.xls"); + + MockMultipartFile mockXlsFile = + new MockMultipartFile( + "file", "create-patent.xls", + MediaType.APPLICATION_OCTET_STREAM_VALUE, new FileInputStream(xlsFile) + ); + LinkedList parameters = new LinkedList<>(); + parameters.add(new DSpaceCommandLineParameter("-c", patents.getID().toString())); + parameters.add(new DSpaceCommandLineParameter("-f", "create-patent.xls")); + + performImportScript(parameters, mockXlsFile, eperson); + + List patentsItems = this.workspaceItemService.findByCollection(context, patents); + assertThat(patentsItems, hasSize(1)); + + Item createdItem = patentsItems.get(0).getItem(); + assertThat("Item expected to be created", createdItem, notNullValue()); + assertThat(createdItem.isArchived(), is(false)); + + List metadata = createdItem.getMetadata(); + assertThat(metadata, hasItems(with("dc.title", "Patent"))); + assertThat(metadata, hasItems(with("dc.contributor.author", "Tom Jones"))); + assertThat(metadata, hasItems(with("dc.contributor.author", "Luca Stone", 1))); + assertThat(metadata, hasItems(with("dc.contributor.author", "Edward Red", 2))); + assertThat(metadata, hasItems(with("dc.publisher", "Publisher"))); + assertThat(metadata, hasItems(with("dc.type", "Patent"))); + + } + + private void performImportScript( + LinkedList parameters, MockMultipartFile xlsFile, EPerson user) + throws Exception { + org.dspace.scripts.Process process = null; + + List list = + parameters.stream() + .map(dSpaceCommandLineParameter -> + dSpaceRunnableParameterConverter.convert(dSpaceCommandLineParameter, Projection.DEFAULT) + ) + .collect(Collectors.toList()); + + try { + AtomicReference idRef = new AtomicReference<>(); + + getClient(getAuthToken(user.getEmail(), password)) + .perform(multipart("/api/system/scripts/bulk-import/processes") + .file(xlsFile) + .param("properties", new ObjectMapper().writeValueAsString(list))) + .andExpect(status().isAccepted()) + .andExpect(jsonPath("$", is( + ProcessMatcher.matchProcess("bulk-import", + String.valueOf(user.getID()), parameters, + ProcessStatus.COMPLETED)))) + .andDo(result -> idRef + .set(read(result.getResponse().getContentAsString(), "$.processId"))); + + process = processService.find(context, idRef.get()); + } finally { + ProcessBuilder.deleteProcess(process.getID()); + } + } + + + private WorkspaceItem findWorkspaceItem(Item item) throws SQLException { + return workspaceItemService.findByItem(context, item); + } + + + private File getXlsFile(String name) { + return new File(BASE_XLS_DIR_PATH, name); + } + +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java new file mode 100644 index 00000000000..83973500ca7 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java @@ -0,0 +1,186 @@ +/** + * 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.bulkedit; + +import static com.jayway.jsonpath.JsonPath.read; +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertTrue; +import static org.hamcrest.Matchers.is; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.sql.SQLException; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.collections4.IteratorUtils; +import org.apache.commons.lang3.StringUtils; +import org.dspace.app.rest.converter.DSpaceRunnableParameterConverter; +import org.dspace.app.rest.matcher.ProcessMatcher; +import org.dspace.app.rest.model.ParameterValueRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.app.rest.test.AbstractEntityIntegrationTest; +import org.dspace.authorize.AuthorizeException; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.ProcessBuilder; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.Item; +import org.dspace.content.ProcessStatus; +import org.dspace.content.Relationship; +import org.dspace.content.service.ItemService; +import org.dspace.content.service.RelationshipService; +import org.dspace.eperson.EPerson; +import org.dspace.scripts.DSpaceCommandLineParameter; +import org.dspace.scripts.service.ProcessService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; + +/** + * Integration tests for the MetadataImport script through ScriptRestRepository API. + * + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public class MetadataImportIT extends AbstractEntityIntegrationTest { + + @Autowired + private ItemService itemService; + @Autowired + private RelationshipService relationshipService; + @Autowired + private ProcessService processService; + @Autowired + private DSpaceRunnableParameterConverter dSpaceRunnableParameterConverter; + + private Collection collection; + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + context.turnOffAuthorisationSystem(); + Community community = CommunityBuilder.createCommunity(context).build(); + collection = + CollectionBuilder.createCollection(context, community) + .withAdminGroup(eperson) + .build(); + context.restoreAuthSystemState(); + } + + @After + public void after() throws SQLException, AuthorizeException { + context.turnOffAuthorisationSystem(); + List relationships = relationshipService.findAll(context); + for (Relationship relationship : relationships) { + relationshipService.delete(context, relationship); + } + context.restoreAuthSystemState(); + } + + @Test + public void metadataImportTest() throws Exception { + String[] csv = {"id,collection,dc.title,dc.contributor.author", + "+," + collection.getHandle() + ",\"Test Import 1\"," + "\"Donald, SmithImported\""}; + + performImportScript(csv, eperson); + + Item importedItem = findItemByName("Test Import 1"); + assertTrue( + StringUtils.equals( + itemService.getMetadata( + importedItem, "dc", "contributor", "author", Item.ANY).get(0).getValue(), "Donald, SmithImported" + ) + ); + + assertEquals(importedItem.getSubmitter(), eperson); + + context.turnOffAuthorisationSystem(); + itemService.delete(context, itemService.find(context, importedItem.getID())); + context.restoreAuthSystemState(); + } + + private void performImportScript(String[] csv, EPerson eperson) throws Exception { + File csvFile = File.createTempFile("dspace-test-import", "csv"); + BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(csvFile), "UTF-8")); + for (String csvLine : csv) { + out.write(csvLine + "\n"); + } + out.flush(); + out.close(); + MockMultipartFile mockCsvFile = + new MockMultipartFile( + "file", "metadata-import.csv", + MediaType.APPLICATION_OCTET_STREAM_VALUE, new FileInputStream(csvFile) + ); + LinkedList parameters = new LinkedList<>(); + parameters.add(new DSpaceCommandLineParameter("-f", "metadata-import.csv")); + parameters.add(new DSpaceCommandLineParameter("-s", "")); + + performImportScript(parameters, mockCsvFile, eperson); + } + + private void performImportScript( + LinkedList parameters, MockMultipartFile csvFile, EPerson user) + throws Exception { + org.dspace.scripts.Process process = null; + + List list = + parameters.stream() + .map(dSpaceCommandLineParameter -> + dSpaceRunnableParameterConverter.convert(dSpaceCommandLineParameter, Projection.DEFAULT) + ) + .collect(Collectors.toList()); + + try { + AtomicReference idRef = new AtomicReference<>(); + + getClient(getAuthToken(user.getEmail(), password)) + .perform(multipart("/api/system/scripts/metadata-import/processes") + .file(csvFile) + .param("properties", new ObjectMapper().writeValueAsString(list))) + .andExpect(status().isAccepted()) + .andExpect(jsonPath("$", is( + ProcessMatcher.matchProcess("metadata-import", + String.valueOf(user.getID()), parameters, + ProcessStatus.COMPLETED)))) + .andDo(result -> idRef + .set(read(result.getResponse().getContentAsString(), "$.processId"))); + + process = processService.find(context, idRef.get()); + } finally { + ProcessBuilder.deleteProcess(process.getID()); + } + } + + private Item findItemByName(String name) throws SQLException { + Item importedItem = null; + List allItems = IteratorUtils.toList(itemService.findAll(context)); + for (Item item : allItems) { + if (item.getName().equals(name)) { + importedItem = item; + } + } + return importedItem; + } + + +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/itemimport/ItemImportIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/itemimport/ItemImportIT.java index f3bbae17be1..7a08cbb841c 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/itemimport/ItemImportIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/itemimport/ItemImportIT.java @@ -48,6 +48,7 @@ import org.dspace.content.Relationship; import org.dspace.content.service.ItemService; import org.dspace.content.service.RelationshipService; +import org.dspace.eperson.EPerson; import org.dspace.scripts.DSpaceCommandLineParameter; import org.dspace.scripts.Process; import org.dspace.scripts.service.ProcessService; @@ -123,7 +124,39 @@ public void importItemByZipSafWithBitstreams() throws Exception { parameters.add(new DSpaceCommandLineParameter("-z", "saf-bitstreams.zip")); MockMultipartFile bitstreamFile = new MockMultipartFile("file", "saf-bitstreams.zip", MediaType.APPLICATION_OCTET_STREAM_VALUE, getClass().getResourceAsStream("saf-bitstreams.zip")); - perfomImportScript(parameters, bitstreamFile); + perfomImportScript(parameters, bitstreamFile, admin); + + checkMetadata(); + checkMetadataWithAnotherSchema(); + checkBitstream(); + + // confirm that TEMP_DIR still exists + File workTempDir = new File(workDir + File.separator + TEMP_DIR); + assertTrue(workTempDir.exists()); + } + + @Test + public void importItemIntoAdministeredCollectionByZipSafWithBitstreams() throws Exception { + + context.turnOffAuthorisationSystem(); + Collection epersonCollection = + CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection") + .withEntityType("Publication") + .withAdminGroup(eperson) + .build(); + context.restoreAuthSystemState(); + + LinkedList parameters = new LinkedList<>(); + parameters.add(new DSpaceCommandLineParameter("-a", "")); + parameters.add(new DSpaceCommandLineParameter("-c", epersonCollection.getID().toString())); + parameters.add(new DSpaceCommandLineParameter("-z", "saf-bitstreams.zip")); + MockMultipartFile bitstreamFile = + new MockMultipartFile( + "file", "saf-bitstreams.zip", + MediaType.APPLICATION_OCTET_STREAM_VALUE, getClass().getResourceAsStream("saf-bitstreams.zip") + ); + perfomImportScript(parameters, bitstreamFile, eperson); checkMetadata(); checkMetadataWithAnotherSchema(); @@ -155,7 +188,7 @@ public void importItemByZipSafWithRelationships() throws Exception { parameters.add(new DSpaceCommandLineParameter("-z", "saf-relationships.zip")); MockMultipartFile bitstreamFile = new MockMultipartFile("file", "saf-relationships.zip", MediaType.APPLICATION_OCTET_STREAM_VALUE, getClass().getResourceAsStream("saf-relationships.zip")); - perfomImportScript(parameters, bitstreamFile); + perfomImportScript(parameters, bitstreamFile, admin); checkMetadata(); checkRelationship(); @@ -222,29 +255,30 @@ private void checkRelationship() throws Exception { .andExpect(jsonPath("$", Matchers.is(RelationshipMatcher.matchRelationship(relationships.get(0))))); } - private void perfomImportScript(LinkedList parameters, MockMultipartFile bitstreamFile) - throws Exception { + private void perfomImportScript( + LinkedList parameters, MockMultipartFile bitstreamFile, EPerson user) + throws Exception { Process process = null; List list = parameters.stream() - .map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter - .convert(dSpaceCommandLineParameter, Projection.DEFAULT)) - .collect(Collectors.toList()); + .map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter + .convert(dSpaceCommandLineParameter, Projection.DEFAULT)) + .collect(Collectors.toList()); try { AtomicReference idRef = new AtomicReference<>(); - getClient(getAuthToken(admin.getEmail(), password)) + getClient(getAuthToken(user.getEmail(), password)) .perform(multipart("/api/system/scripts/import/processes") - .file(bitstreamFile) - .param("properties", new ObjectMapper().writeValueAsString(list))) + .file(bitstreamFile) + .param("properties", new ObjectMapper().writeValueAsString(list))) .andExpect(status().isAccepted()) .andExpect(jsonPath("$", is( - ProcessMatcher.matchProcess("import", - String.valueOf(admin.getID()), parameters, - ProcessStatus.COMPLETED)))) + ProcessMatcher.matchProcess("import", + String.valueOf(user.getID()), parameters, + ProcessStatus.COMPLETED)))) .andDo(result -> idRef - .set(read(result.getResponse().getContentAsString(), "$.processId"))); + .set(read(result.getResponse().getContentAsString(), "$.processId"))); process = processService.find(context, idRef.get()); checkProcess(process); diff --git a/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java b/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java index 37b54f48123..6668d7b328a 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java @@ -286,6 +286,61 @@ public void curateScript_validRequest_Task() throws Exception { } } + @Test + public void curateScript_collectionAdmin_Test() throws Exception { + context.turnOffAuthorisationSystem(); + + String token = getAuthToken(eperson.getEmail(), password); + AtomicReference idRef = new AtomicReference<>(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = + CollectionBuilder.createCollection(context, child1) + .withAdminGroup(context.reloadEntity(eperson)) + .withName("Collection 1") + .build(); + + Item publicItem1 = ItemBuilder.createItem(context, col1) + .withTitle("Public item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + LinkedList parameters = new LinkedList<>(); + + parameters.add(new DSpaceCommandLineParameter("-i", publicItem1.getHandle())); + parameters.add(new DSpaceCommandLineParameter("-s", "open")); + parameters.add(new DSpaceCommandLineParameter("-t", CurationClientOptions.getTaskOptions().get(0))); + + List list = parameters.stream() + .map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter + .convert(dSpaceCommandLineParameter, Projection.DEFAULT)) + .collect(Collectors.toList()); + + context.restoreAuthSystemState(); + + try { + getClient(token) + .perform(multipart(CURATE_SCRIPT_ENDPOINT) + .param("properties", new ObjectMapper().writeValueAsString(list))) + .andExpect(status().isAccepted()) + .andExpect(jsonPath("$", is( + ProcessMatcher.matchProcess("curate", + String.valueOf(eperson.getID()), parameters, + ProcessStatus.COMPLETED)))) + .andDo(result -> idRef + .set(read(result.getResponse().getContentAsString(), "$.processId"))); + } finally { + ProcessBuilder.deleteProcess(idRef.get()); + } + } + @Test public void curateScript_validRequest_TaskFile() throws Exception { context.turnOffAuthorisationSystem(); From 2faf90ded00d779f27b46d0e6fed3737eec1b01f Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Wed, 11 Sep 2024 16:26:19 +0200 Subject: [PATCH 6/6] [DSC-1905] Removes process-cleaner for non administrators --- .../administer/ProcessCleanerConfiguration.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/administer/ProcessCleanerConfiguration.java b/dspace-api/src/main/java/org/dspace/administer/ProcessCleanerConfiguration.java index e765c2a4c18..91dcfb5dfec 100644 --- a/dspace-api/src/main/java/org/dspace/administer/ProcessCleanerConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/administer/ProcessCleanerConfiguration.java @@ -7,12 +7,7 @@ */ package org.dspace.administer; -import java.sql.SQLException; -import java.util.List; - import org.apache.commons.cli.Options; -import org.dspace.core.Context; -import org.dspace.scripts.DSpaceCommandLineParameter; import org.dspace.scripts.configuration.ScriptConfiguration; /** @@ -22,17 +17,6 @@ public class ProcessCleanerConfiguration extends Scrip private Class dspaceRunnableClass; - @Override - public boolean isAllowedToExecute(Context context, List commandLineParameters) { - try { - return authorizeService.isAdmin(context) || authorizeService.isComColAdmin(context) || - authorizeService.isItemAdmin(context); - } catch (SQLException e) { - throw new RuntimeException( - "SQLException occurred when checking if the current user is eligible to run the script", e); - } - } - @Override public Options getOptions() { if (options == null) {