[CST-5755] Ported process special group tracking

This commit is contained in:
Jurgen Mamani
2022-04-28 14:17:15 +02:00
parent 365827ce92
commit e4a4b913b9
13 changed files with 230 additions and 12 deletions

View File

@@ -259,12 +259,19 @@ public class Curation extends DSpaceRunnable<CurationScriptConfiguration> {
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.

View File

@@ -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<Integer> {
)
private List<Bitstream> bitstreams;
@ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST})
@JoinTable(
name = "process2group",
joinColumns = {@JoinColumn(name = "process_id")},
inverseJoinColumns = {@JoinColumn(name = "group_id")}
)
private final List<Group> groups = new ArrayList<>();
@Column(name = "creation_time", nullable = false)
@Temporal(TemporalType.TIMESTAMP)
private Date creationTime;
@@ -211,6 +221,14 @@ public class Process implements ReloadableEntity<Integer> {
return creationTime;
}
public List<Group> getGroups() {
return groups;
}
public void addGroup(Group group) {
this.groups.add(group);
}
/**
* Return <code>true</code> if <code>other</code> is the same Process
* as this object, <code>false</code> otherwise

View File

@@ -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<DSpaceCommandLineParameter> parameters) throws SQLException {
List<DSpaceCommandLineParameter> parameters, final List<Group> 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<Group> 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);
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);
}

View File

@@ -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<UUID> getSpecialGroups();
}

View File

@@ -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<UUID> getSpecialGroups() {
return Collections.emptyList();
}
}

View File

@@ -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<DSpaceCommandLineParameter> parameters) throws SQLException;
List<DSpaceCommandLineParameter> parameters, final List<Group> specialGroups) throws SQLException;
/**
* This method will retrieve a Process object from the Database with the given ID

View File

@@ -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
);

View File

@@ -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
);

View File

@@ -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
);

View File

@@ -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<Process, ProcessService> {
List<DSpaceCommandLineParameter> 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<DSpaceCommandLineParameter> parameters,
List<Group> 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<DSpaceCommandLineParameter> parameters)
List<DSpaceCommandLineParameter> parameters, final List<Group> 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;
}

View File

@@ -109,7 +109,8 @@ public class ScriptRestRepository extends DSpaceRestRepository<ScriptRest, Strin
throw new AuthorizeException("Current user is not eligible to execute script with name: " + scriptName);
}
RestDSpaceRunnableHandler restDSpaceRunnableHandler = new RestDSpaceRunnableHandler(
context.getCurrentUser(), scriptToExecute.getName(), dSpaceCommandLineParameters);
context.getCurrentUser(), scriptToExecute.getName(), dSpaceCommandLineParameters,
context.getSpecialGroups());
List<String> args = constructArgs(dSpaceCommandLineParameters);
runDSpaceScript(files, context, scriptToExecute, restDSpaceRunnableHandler, args);
return converter.toRest(restDSpaceRunnableHandler.getProcess(context), utils.obtainProjection());

View File

@@ -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<DSpaceCommandLineParameter> parameters) {
public RestDSpaceRunnableHandler(EPerson ePerson, String scriptName, List<DSpaceCommandLineParameter> parameters,
final List<Group> 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<UUID> getSpecialGroups() {
Context context = new Context();
List<UUID> 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;
}
}

View File

@@ -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<ScriptConfiguration> 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<DSpaceCommandLineParameter> parameters = new LinkedList<>();
parameters.add(new DSpaceCommandLineParameter("-r", "test"));
parameters.add(new DSpaceCommandLineParameter("-i", null));
List<ParameterValueRest> list = parameters.stream()
.map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter
.convert(dSpaceCommandLineParameter, Projection.DEFAULT))
.collect(Collectors.toList());
String token = getAuthToken(admin.getEmail(), password);
List<ProcessStatus> acceptableProcessStatuses = new LinkedList<>();
acceptableProcessStatuses.addAll(Arrays.asList(ProcessStatus.SCHEDULED,
ProcessStatus.RUNNING,
ProcessStatus.COMPLETED));
AtomicReference<Integer> 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<Group> 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 {