diff --git a/dspace-api/src/main/java/org/dspace/curate/Curation.java b/dspace-api/src/main/java/org/dspace/curate/Curation.java index a01c731189..0fcbd6c272 100644 --- a/dspace-api/src/main/java/org/dspace/curate/Curation.java +++ b/dspace-api/src/main/java/org/dspace/curate/Curation.java @@ -259,12 +259,19 @@ public class Curation extends DSpaceRunnable { super.handler.logError("EPerson not found: " + currentUserUuid); throw new IllegalArgumentException("Unable to find a user with uuid: " + currentUserUuid); } + assignSpecialGroupsInContext(); this.context.setCurrentUser(eperson); } catch (SQLException e) { handler.handleException("Something went wrong trying to fetch eperson for uuid: " + currentUserUuid, e); } } + protected void assignSpecialGroupsInContext() throws SQLException { + for (UUID uuid : handler.getSpecialGroups()) { + context.setSpecialGroup(uuid); + } + } + /** * Fills in some optional command line options. * Checks if there are missing required options or invalid values for options. diff --git a/dspace-api/src/main/java/org/dspace/scripts/Process.java b/dspace-api/src/main/java/org/dspace/scripts/Process.java index b15fd0c84c..34980fa512 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/Process.java +++ b/dspace-api/src/main/java/org/dspace/scripts/Process.java @@ -10,6 +10,7 @@ package org.dspace.scripts; import java.util.ArrayList; import java.util.Date; import java.util.List; +import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; @@ -33,6 +34,7 @@ import org.dspace.content.Bitstream; import org.dspace.content.ProcessStatus; import org.dspace.core.ReloadableEntity; import org.dspace.eperson.EPerson; +import org.dspace.eperson.Group; /** * This class is the DB Entity representation of the Process object to be stored in the Database @@ -77,6 +79,14 @@ public class Process implements ReloadableEntity { ) private List bitstreams; + @ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST}) + @JoinTable( + name = "process2group", + joinColumns = {@JoinColumn(name = "process_id")}, + inverseJoinColumns = {@JoinColumn(name = "group_id")} + ) + private final List groups = new ArrayList<>(); + @Column(name = "creation_time", nullable = false) @Temporal(TemporalType.TIMESTAMP) private Date creationTime; @@ -211,6 +221,14 @@ public class Process implements ReloadableEntity { return creationTime; } + public List getGroups() { + return groups; + } + + public void addGroup(Group group) { + this.groups.add(group); + } + /** * Return true if other is the same Process * as this object, false otherwise diff --git a/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java b/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java index 8c03a9767d..244f6f13ce 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java @@ -21,6 +21,8 @@ import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.regex.Pattern; @@ -43,7 +45,9 @@ import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.LogHelper; import org.dspace.eperson.EPerson; +import org.dspace.eperson.Group; import org.dspace.eperson.service.EPersonService; +import org.dspace.eperson.service.GroupService; import org.dspace.scripts.service.ProcessService; import org.springframework.beans.factory.annotation.Autowired; @@ -72,15 +76,26 @@ public class ProcessServiceImpl implements ProcessService { @Autowired private EPersonService ePersonService; + @Autowired + private GroupService groupService; + @Override public Process create(Context context, EPerson ePerson, String scriptName, - List parameters) throws SQLException { + List parameters, final List specialGroups) throws SQLException { Process process = new Process(); process.setEPerson(ePerson); process.setName(scriptName); process.setParameters(DSpaceCommandLineParameter.concatenate(parameters)); process.setCreationTime(new Date()); + Optional.ofNullable(specialGroups) + .ifPresent(sg -> { + Set specialGroupsSet = new HashSet<>(sg); + for (Group group : specialGroupsSet) { + process.addGroup(group); + } + }); + Process createdProcess = processDAO.create(context, process); log.info(LogHelper.getHeader(context, "process_create", "Process has been created for eperson with email " + ePerson.getEmail() @@ -162,10 +177,24 @@ public class ProcessServiceImpl implements ProcessService { MetadataField dspaceProcessFileTypeField = metadataFieldService .findByString(context, Process.BITSTREAM_TYPE_METADATAFIELD, '.'); bitstreamService.addMetadata(context, bitstream, dspaceProcessFileTypeField, null, type); - authorizeService.addPolicy(context, bitstream, Constants.READ, context.getCurrentUser()); - authorizeService.addPolicy(context, bitstream, Constants.WRITE, context.getCurrentUser()); - authorizeService.addPolicy(context, bitstream, Constants.DELETE, context.getCurrentUser()); - bitstreamService.update(context, bitstream); + if (Objects.isNull(context.getCurrentUser())) { + Group anonymous = groupService.findByName(context, Group.ANONYMOUS); + authorizeService.addPolicy(context, bitstream, Constants.READ, anonymous); + } else { + authorizeService.addPolicy(context, bitstream, Constants.READ, context.getCurrentUser()); + authorizeService.addPolicy(context, bitstream, Constants.WRITE, context.getCurrentUser()); + authorizeService.addPolicy(context, bitstream, Constants.DELETE, context.getCurrentUser()); + } + + try { + context.turnOffAuthorisationSystem(); + bitstreamService.update(context, bitstream); + context.restoreAuthSystemState(); + } catch (SQLException | AuthorizeException e) { + log.info(e.getMessage()); + throw new RuntimeException(e.getMessage(), e); + } + process.addBitstream(bitstream); update(context, process); } diff --git a/dspace-api/src/main/java/org/dspace/scripts/handler/DSpaceRunnableHandler.java b/dspace-api/src/main/java/org/dspace/scripts/handler/DSpaceRunnableHandler.java index f1b37cade2..98f2fdd041 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/handler/DSpaceRunnableHandler.java +++ b/dspace-api/src/main/java/org/dspace/scripts/handler/DSpaceRunnableHandler.java @@ -10,7 +10,9 @@ package org.dspace.scripts.handler; import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; +import java.util.List; import java.util.Optional; +import java.util.UUID; import org.apache.commons.cli.Options; import org.dspace.authorize.AuthorizeException; @@ -114,4 +116,6 @@ public interface DSpaceRunnableHandler { */ public void writeFilestream(Context context, String fileName, InputStream inputStream, String type) throws IOException, SQLException, AuthorizeException; + + public List getSpecialGroups(); } diff --git a/dspace-api/src/main/java/org/dspace/scripts/handler/impl/CommandLineDSpaceRunnableHandler.java b/dspace-api/src/main/java/org/dspace/scripts/handler/impl/CommandLineDSpaceRunnableHandler.java index 6a108728d4..8a7f41d958 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/handler/impl/CommandLineDSpaceRunnableHandler.java +++ b/dspace-api/src/main/java/org/dspace/scripts/handler/impl/CommandLineDSpaceRunnableHandler.java @@ -10,7 +10,10 @@ package org.dspace.scripts.handler.impl; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.Collections; +import java.util.List; import java.util.Optional; +import java.util.UUID; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Options; @@ -113,4 +116,9 @@ public class CommandLineDSpaceRunnableHandler implements DSpaceRunnableHandler { File file = new File(fileName); FileUtils.copyInputStreamToFile(inputStream, file); } + + @Override + public List getSpecialGroups() { + return Collections.emptyList(); + } } diff --git a/dspace-api/src/main/java/org/dspace/scripts/service/ProcessService.java b/dspace-api/src/main/java/org/dspace/scripts/service/ProcessService.java index 27c0c75a35..0e234c197a 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/service/ProcessService.java +++ b/dspace-api/src/main/java/org/dspace/scripts/service/ProcessService.java @@ -16,6 +16,7 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; import org.dspace.core.Context; import org.dspace.eperson.EPerson; +import org.dspace.eperson.Group; import org.dspace.scripts.DSpaceCommandLineParameter; import org.dspace.scripts.Process; import org.dspace.scripts.ProcessLogLevel; @@ -32,11 +33,12 @@ public interface ProcessService { * @param ePerson The ePerson for which this process will be created on * @param scriptName The script name to be used for the process * @param parameters The parameters to be used for the process + * @param specialGroups List Of special groups to be stored together with the process * @return The created process * @throws SQLException If something goes wrong */ public Process create(Context context, EPerson ePerson, String scriptName, - List parameters) throws SQLException; + List parameters, final List specialGroups) throws SQLException; /** * This method will retrieve a Process object from the Database with the given ID diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.0_2021.03.26__process_to_group.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.0_2021.03.26__process_to_group.sql new file mode 100644 index 0000000000..f862772af9 --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.0_2021.03.26__process_to_group.sql @@ -0,0 +1,17 @@ +-- +-- 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/ +-- + +------------------------------------------------------------------------------- +-- Sequences for Process within Group feature +------------------------------------------------------------------------------- + +CREATE TABLE Process2Group +( + process_id INTEGER REFERENCES Process(process_id), + group_id UUID REFERENCES epersongroup (uuid) ON DELETE CASCADE +); \ No newline at end of file diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.0_2021.03.26__process_to_group.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.0_2021.03.26__process_to_group.sql new file mode 100644 index 0000000000..f862772af9 --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.0_2021.03.26__process_to_group.sql @@ -0,0 +1,17 @@ +-- +-- 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/ +-- + +------------------------------------------------------------------------------- +-- Sequences for Process within Group feature +------------------------------------------------------------------------------- + +CREATE TABLE Process2Group +( + process_id INTEGER REFERENCES Process(process_id), + group_id UUID REFERENCES epersongroup (uuid) ON DELETE CASCADE +); \ No newline at end of file diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.0_2021.03.26__process_to_group.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.0_2021.03.26__process_to_group.sql new file mode 100644 index 0000000000..f862772af9 --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.0_2021.03.26__process_to_group.sql @@ -0,0 +1,17 @@ +-- +-- 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/ +-- + +------------------------------------------------------------------------------- +-- Sequences for Process within Group feature +------------------------------------------------------------------------------- + +CREATE TABLE Process2Group +( + process_id INTEGER REFERENCES Process(process_id), + group_id UUID REFERENCES epersongroup (uuid) ON DELETE CASCADE +); \ No newline at end of file diff --git a/dspace-api/src/test/java/org/dspace/builder/ProcessBuilder.java b/dspace-api/src/test/java/org/dspace/builder/ProcessBuilder.java index 981ce63493..b553f425d3 100644 --- a/dspace-api/src/test/java/org/dspace/builder/ProcessBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/ProcessBuilder.java @@ -17,6 +17,7 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.content.ProcessStatus; import org.dspace.core.Context; import org.dspace.eperson.EPerson; +import org.dspace.eperson.Group; import org.dspace.scripts.DSpaceCommandLineParameter; import org.dspace.scripts.Process; import org.dspace.scripts.service.ProcessService; @@ -33,14 +34,22 @@ public class ProcessBuilder extends AbstractBuilder { List parameters) throws SQLException { ProcessBuilder processBuilder = new ProcessBuilder(context); - return processBuilder.create(context, ePerson, scriptName, parameters); + return processBuilder.create(context, ePerson, scriptName, parameters, null); + } + + public static ProcessBuilder createProcess(Context context, EPerson ePerson, String scriptName, + List parameters, + List specialGroups) + throws SQLException { + ProcessBuilder processBuilder = new ProcessBuilder(context); + return processBuilder.create(context, ePerson, scriptName, parameters, specialGroups); } private ProcessBuilder create(Context context, EPerson ePerson, String scriptName, - List parameters) + List parameters, final List specialGroups) throws SQLException { this.context = context; - this.process = processService.create(context, ePerson, scriptName, parameters); + this.process = processService.create(context, ePerson, scriptName, parameters, specialGroups); this.process.setProcessStatus(ProcessStatus.SCHEDULED); return this; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ScriptRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ScriptRestRepository.java index 096eeedb9e..54dd578af5 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ScriptRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ScriptRestRepository.java @@ -109,7 +109,8 @@ public class ScriptRestRepository extends DSpaceRestRepository args = constructArgs(dSpaceCommandLineParameters); runDSpaceScript(files, context, scriptToExecute, restDSpaceRunnableHandler, args); return converter.toRest(restDSpaceRunnableHandler.getProcess(context), utils.obtainProjection()); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/scripts/handler/impl/RestDSpaceRunnableHandler.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/scripts/handler/impl/RestDSpaceRunnableHandler.java index 5b778c47fd..772325a4f4 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/scripts/handler/impl/RestDSpaceRunnableHandler.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/scripts/handler/impl/RestDSpaceRunnableHandler.java @@ -12,6 +12,7 @@ import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -27,6 +28,7 @@ import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.BitstreamService; import org.dspace.core.Context; import org.dspace.eperson.EPerson; +import org.dspace.eperson.Group; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.EPersonService; import org.dspace.scripts.DSpaceCommandLineParameter; @@ -59,12 +61,14 @@ public class RestDSpaceRunnableHandler implements DSpaceRunnableHandler { * @param ePerson The eperson that creates the process * @param scriptName The name of the script for which is a process will be created * @param parameters The parameters for this process + * @param specialGroups specialGroups The list of special groups related to eperson creating process at process creation time */ - public RestDSpaceRunnableHandler(EPerson ePerson, String scriptName, List parameters) { + public RestDSpaceRunnableHandler(EPerson ePerson, String scriptName, List parameters, + final List specialGroups) { Context context = new Context(); try { ePersonId = ePerson.getID(); - Process process = processService.create(context, ePerson, scriptName, parameters); + Process process = processService.create(context, ePerson, scriptName, parameters, specialGroups); processId = process.getID(); this.scriptName = process.getName(); @@ -304,4 +308,24 @@ public class RestDSpaceRunnableHandler implements DSpaceRunnableHandler { log.error("RestDSpaceRunnableHandler with process: " + processId + " could not write log to process", e); } } + + @Override + public List getSpecialGroups() { + Context context = new Context(); + List specialGroups = new ArrayList<>(); + try { + Process process = processService.find(context, processId); + for (Group group : process.getGroups()) { + context.setSpecialGroup(group.getID()); + specialGroups.add(group.getID()); + } + } catch (SQLException e) { + log.error("RestDSpaceRunnableHandler with process: " + processId + " could not find the process", e); + } finally { + if (context.isValid()) { + context.abort(); + } + } + return specialGroups; + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScriptRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScriptRestRepositoryIT.java index bf70eb2e0b..55d2b3aa2e 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScriptRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScriptRestRepositoryIT.java @@ -14,6 +14,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -42,6 +43,7 @@ import org.dspace.app.rest.test.AbstractControllerIntegrationTest; import org.dspace.authorize.AuthorizeException; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.GroupBuilder; import org.dspace.builder.ItemBuilder; import org.dspace.builder.ProcessBuilder; import org.dspace.content.Bitstream; @@ -50,10 +52,12 @@ import org.dspace.content.Community; import org.dspace.content.Item; import org.dspace.content.ProcessStatus; import org.dspace.content.service.BitstreamService; +import org.dspace.eperson.Group; import org.dspace.scripts.DSpaceCommandLineParameter; import org.dspace.scripts.Process; import org.dspace.scripts.configuration.ScriptConfiguration; import org.dspace.scripts.service.ProcessService; +import org.dspace.services.ConfigurationService; import org.hamcrest.CoreMatchers; import org.hamcrest.Matchers; import org.junit.After; @@ -71,6 +75,9 @@ public class ScriptRestRepositoryIT extends AbstractControllerIntegrationTest { @Autowired private BitstreamService bitstreamService; + @Autowired + private ConfigurationService configurationService; + @Autowired private List scriptConfigurations; @@ -521,6 +528,64 @@ public class ScriptRestRepositoryIT extends AbstractControllerIntegrationTest { ) )); } + @Test + public void TrackSpecialGroupduringprocessSchedulingTest() throws Exception { + context.turnOffAuthorisationSystem(); + + Group specialGroup = GroupBuilder.createGroup(context) + .withName("Special Group") + .addMember(admin) + .build(); + + context.restoreAuthSystemState(); + + configurationService.setProperty("authentication-password.login.specialgroup", specialGroup.getName()); + + LinkedList parameters = new LinkedList<>(); + + parameters.add(new DSpaceCommandLineParameter("-r", "test")); + parameters.add(new DSpaceCommandLineParameter("-i", null)); + + List list = parameters.stream() + .map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter + .convert(dSpaceCommandLineParameter, Projection.DEFAULT)) + .collect(Collectors.toList()); + + + + String token = getAuthToken(admin.getEmail(), password); + List acceptableProcessStatuses = new LinkedList<>(); + acceptableProcessStatuses.addAll(Arrays.asList(ProcessStatus.SCHEDULED, + ProcessStatus.RUNNING, + ProcessStatus.COMPLETED)); + + AtomicReference idRef = new AtomicReference<>(); + + try { + getClient(token).perform(post("/api/system/scripts/mock-script/processes") + .contentType("multipart/form-data") + .param("properties", new Gson().toJson(list))) + .andExpect(status().isAccepted()) + .andExpect(jsonPath("$", is(ProcessMatcher.matchProcess("mock-script", + String.valueOf(admin.getID()), + parameters, acceptableProcessStatuses)))) + .andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.processId"))); + + Process process = processService.find(context, idRef.get()); + List groups = process.getGroups(); + boolean isPresent = false; + for (Group group : groups) { + if (group.getID().equals(specialGroup.getID())) { + isPresent = true; + } + } + assertTrue(isPresent); + + } finally { + ProcessBuilder.deleteProcess(idRef.get()); + } + } + @After public void destroy() throws Exception {