diff --git a/pom.xml b/pom.xml index 60a5d582c47501154054ba3bc8e41927cdbb2922..a9f07d9c2789b89324fc970488c255c2ee2eccde 100644 --- a/pom.xml +++ b/pom.xml @@ -252,11 +252,11 @@ <artifactId>svenson</artifactId> <version>1.5.8</version> </dependency> - <dependency> + <!-- <dependency> <groupId>eu.hbp.mip</groupId> <artifactId>portal-backend</artifactId> <version>4.0.0</version> - </dependency> + </dependency>--> </dependencies> <build> @@ -270,7 +270,7 @@ <include>**/*.csv</include> <include>**/*.sql</include> <include>**/*.conf</include> - <include>**/*.yml</include> <!-- Only for development --> + <!-- <include>**/*.yml</include> Only for development --> </includes> <filtering>true</filtering> </resource> diff --git a/src/main/java/eu/hbp/mip/controllers/ExperimentAPI.java b/src/main/java/eu/hbp/mip/controllers/ExperimentAPI.java index 78f6032e06beb3aa0607a03553975b2a5c72037c..886c59232cf16442d64010fb6e934e93cff2e69d 100644 --- a/src/main/java/eu/hbp/mip/controllers/ExperimentAPI.java +++ b/src/main/java/eu/hbp/mip/controllers/ExperimentAPI.java @@ -40,16 +40,18 @@ public class ExperimentAPI { @RequestParam(name = "algorithm", required = false) String algorithm, @RequestParam(name = "shared", required = false) Boolean shared, @RequestParam(name = "viewed", required = false) Boolean viewed, + @RequestParam(name = "includeShared", required = false, defaultValue = "true") boolean includeShared, @RequestParam(name = "orderBy", required = false, defaultValue = "created") String orderBy, @RequestParam(name = "descending", required = false, defaultValue = "true") Boolean descending, @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "3") int size + @RequestParam(defaultValue = "10") int size ) { Map experiments = experimentService.getExperiments( name, algorithm, shared, viewed, + includeShared, page, size, orderBy, diff --git a/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java b/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java index 1cf4864bdb1a1d6a568831eb93f5b6ce7f1168cd..f43c34960fd89d9d8036fd215fd51b0741de648f 100644 --- a/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java +++ b/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java @@ -86,7 +86,6 @@ public class ExperimentDAO { } public enum Type { - ERROR("text/plain+error"), WARNING("text/plain+warning"), USER_WARNING("text/plain+user_error"), HIGHCHARTS("application/vnd.highcharts+json"), @@ -115,7 +114,7 @@ public class ExperimentDAO { */ } - public ExperimentDTO convertToDTO() + public ExperimentDTO convertToDTO(boolean includeResult) { ExperimentDTO experimentDTO = new ExperimentDTO(); experimentDTO.setAlgorithmDetails(JsonConverters.convertJsonStringToObject(this.algorithmDetails, AlgorithmDTO.class)); @@ -124,7 +123,9 @@ public class ExperimentDAO { experimentDTO.setFinished(this.finished); experimentDTO.setCreatedBy(this.createdBy.getUsername()); experimentDTO.setName(this.name); - experimentDTO.setResult(convertJsonStringToResult(this.result)); + if(includeResult){ + experimentDTO.setResult(JsonConverters.convertJsonStringToObject(String.valueOf(this.result), new ArrayList<ExperimentDTO.ResultDTO>().getClass())); + } experimentDTO.setStatus(this.status); experimentDTO.setShared(this.shared); experimentDTO.setUuid(this.uuid); @@ -132,13 +133,6 @@ public class ExperimentDAO { return experimentDTO; } - public static Map convertJsonStringToResult(String jsonResult) { - if(jsonResult == null || jsonResult.isEmpty()) - return null; - JSONParser parser = new JSONParser(); - parser.addTypeHint("Result[]", ExperimentDTO.ResultDTO.class); - return parser.parse(Map.class, jsonResult); - } public String getAlgorithmDetails() { return algorithmDetails; } diff --git a/src/main/java/eu/hbp/mip/models/DTOs/ExperimentDTO.java b/src/main/java/eu/hbp/mip/models/DTOs/ExperimentDTO.java index 8c6073a1f0d0b6f1cb3db26deb1e7b4e8586daeb..5707b75d04fa575d7f11ef1f4bf15359a1d0cc8d 100644 --- a/src/main/java/eu/hbp/mip/models/DTOs/ExperimentDTO.java +++ b/src/main/java/eu/hbp/mip/models/DTOs/ExperimentDTO.java @@ -1,11 +1,15 @@ package eu.hbp.mip.models.DTOs; +import com.fasterxml.jackson.annotation.JsonInclude; import eu.hbp.mip.models.DAOs.ExperimentDAO; +import java.lang.reflect.Array; import java.util.Date; +import java.util.List; import java.util.Map; import java.util.UUID; +@JsonInclude(JsonInclude.Include.NON_NULL) public class ExperimentDTO { private UUID uuid; @@ -16,7 +20,7 @@ public class ExperimentDTO { private Date finished; private Boolean shared; private Boolean viewed; - private Map result; + private List<ExperimentDTO.ResultDTO> result; private ExperimentDAO.Status status; private String algorithm; @@ -106,11 +110,11 @@ public class ExperimentDTO { this.viewed = viewed; } - public Map getResult() { + public List<ExperimentDTO.ResultDTO> getResult() { return result; } - public void setResult(Map result) { + public void setResult(List<ExperimentDTO.ResultDTO> result) { this.result = result; } diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index ed5c8edd87775c3f94607ec9c0515dd5de72b518..a79db101653eaeda2dc39082b53bf745308a0a7c 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -74,6 +74,7 @@ public class ExperimentService { * @param algorithm is optional, in case it is required to filter the experiments by algorithm name * @param shared is optional, in case it is required to filter the experiments by shared * @param viewed is optional, in case it is required to filter the experiments by viewed + * @param includeShared is optional, in case it is required to retrieve the experiment that is shared * @param page is the page that is required to be retrieve * @param size is the size of each page * @param orderBy is the column that is required to ordered by @@ -82,18 +83,29 @@ public class ExperimentService { * @return a list of mapped experiments */ - public Map getExperiments(String name, String algorithm, Boolean shared, Boolean viewed, int page, int size, String orderBy, Boolean descending, String endpoint) { + public Map getExperiments(String name, String algorithm, Boolean shared, Boolean viewed, boolean includeShared, int page, int size, String orderBy, Boolean descending, String endpoint) { UserDAO user = activeUserService.getActiveUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Listing my experiments."); - if (size > 10) - throw new BadRequestException("Invalid size input, max size is 10."); - + if (size > 50) + throw new BadRequestException("Invalid size input, max size is 50."); + /*TODO:if(role == master){ Specification<ExperimentDAO> spec = Specification .where(new ExperimentSpecifications.ExperimentWithName(name)) .and(new ExperimentSpecifications.ExperimentWithAlgorithm(algorithm)) .and(new ExperimentSpecifications.ExperimentWithShared(shared)) .and(new ExperimentSpecifications.ExperimentWithViewed(viewed)) .and(new ExperimentSpecifications.ExperimentOrderBy(orderBy, descending)); + } + else + */ + Specification<ExperimentDAO> spec = Specification + .where(new ExperimentSpecifications.MyExperiment(user.getUsername())) + .or(new ExperimentSpecifications.SharedExperiment(includeShared)) + .and(new ExperimentSpecifications.ExperimentWithAlgorithm(algorithm)) + .and(new ExperimentSpecifications.ExperimentWithShared(shared)) + .and(new ExperimentSpecifications.ExperimentWithViewed(viewed)) + .and(new ExperimentSpecifications.ExperimentWithName(name)) + .and(new ExperimentSpecifications.ExperimentOrderBy(orderBy, descending)); Pageable paging = PageRequest.of(page, size); Page<ExperimentDAO> pageExperiments = experimentRepository.findAll(spec, paging); @@ -103,7 +115,7 @@ public class ExperimentService { throw new NoContent("No experiment found with the filters provided."); List<ExperimentDTO> experimentDTOs = new ArrayList<>(); - experimentDAOs.forEach(experimentDAO -> experimentDTOs.add(experimentDAO.convertToDTO())); + experimentDAOs.forEach(experimentDAO -> experimentDTOs.add(experimentDAO.convertToDTO(false))); Map<String, Object> response = new HashMap<>(); response.put("experiments", experimentDTOs); @@ -129,12 +141,12 @@ public class ExperimentService { Logging.LogUserAction(user.getUsername(), endpoint, "Loading Experiment with uuid : " + uuid); experimentDAO = loadExperiment(uuid, endpoint); - - if (!experimentDAO.isShared() && !experimentDAO.getCreatedBy().getUsername().equals(user.getUsername())) { + //TODO: if (!experimentDAO.isShared() && !experimentDAO.getCreatedBy().getUsername().equals(user.getUsername()) && if not master) { + if (!experimentDAO.isShared() && !experimentDAO.getCreatedBy().getUsername().equals(user.getUsername()) ) { Logging.LogUserAction(user.getUsername(), endpoint, "Accessing Experiment is unauthorized."); throw new UnauthorizedException("You don't have access to the experiment."); } - ExperimentDTO experimentDTO = experimentDAO.convertToDTO(); + ExperimentDTO experimentDTO = experimentDAO.convertToDTO(true); Logging.LogUserAction(user.getUsername(), endpoint, "Experiment was Loaded with uuid : " + uuid + "."); return experimentDTO; @@ -161,7 +173,7 @@ public class ExperimentService { if (authenticationIsEnabled) { String experimentDatasets = getDatasetFromExperimentParameters(experimentDTO, endpoint); - ClaimUtils.validateAccessRightsOnDatasets(user.getUsername(), authentication.getAuthorities(), experimentDatasets); + ClaimUtils.validateAccessRightsOnDatasets(user.getUsername(), authentication.getAuthorities(), experimentDatasets, endpoint); } // Run with the appropriate engine @@ -205,7 +217,7 @@ public class ExperimentService { if (authenticationIsEnabled) { String experimentDatasets = getDatasetFromExperimentParameters(experimentDTO, endpoint); - ClaimUtils.validateAccessRightsOnDatasets(user.getUsername(), authentication.getAuthorities(), experimentDatasets); + ClaimUtils.validateAccessRightsOnDatasets(user.getUsername(), authentication.getAuthorities(), experimentDatasets, endpoint); } String body = gson.toJson(algorithmParameters); @@ -267,7 +279,7 @@ public class ExperimentService { Logging.LogUserAction(user.getUsername(), endpoint, "Updated experiment with uuid : " + uuid + "."); - experimentDTO = experimentDAO.convertToDTO(); + experimentDTO = experimentDAO.convertToDTO(true); return experimentDTO; } @@ -324,37 +336,37 @@ public class ExperimentService { } private void verifyPatchExperimentNonEditableFields(String uuid, ExperimentDTO experimentDTO, ExperimentDAO experimentDAO, String endpoint) { - if (experimentDTO.getUuid() != null && experimentDTO.getUuid().toString().compareTo(uuid) != 0) { + if (experimentDTO.getUuid() != null ) { Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "Uuid is not editable."); throw new BadRequestException("Uuid is not editable."); } - if (experimentDTO.getAlgorithm() != null && experimentDTO.getAlgorithm().compareTo(experimentDAO.getAlgorithm()) != 0) { + if (experimentDTO.getAlgorithm() != null ) { Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "Algorithm is not editable."); throw new BadRequestException("Algorithm is not editable."); } - if (experimentDTO.getCreated() != null && experimentDTO.getCreatedBy().compareTo(experimentDAO.getCreatedBy().getUsername()) != 0) { + if (experimentDTO.getCreated() != null) { Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "CreatedBy is not editable."); throw new BadRequestException("CreatedBy is not editable."); } - if (experimentDTO.getAlgorithmDetails() != null && JsonConverters.convertObjectToJsonString(experimentDTO.getAlgorithmDetails()).compareTo(experimentDAO.getAlgorithmDetails()) != 0) { + if (experimentDTO.getAlgorithmDetails() != null) { Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "AlgorithmDetails is not editable."); throw new BadRequestException("AlgorithmDetails is not editable."); } - if (experimentDTO.getCreated() != null && experimentDTO.getAlgorithm().compareTo(experimentDAO.getAlgorithm()) != 0) { + if (experimentDTO.getCreated() != null) { Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "Created is not editable."); throw new BadRequestException("Created is not editable."); } - if (experimentDTO.getResult() != null && JsonConverters.convertObjectToJsonString(experimentDTO.getResult()).compareTo(experimentDAO.getResult()) != 0) { + if (experimentDTO.getResult() != null) { Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "Status is not editable."); throw new BadRequestException("Status is not editable."); } - if (experimentDTO.getStatus() != null && experimentDTO.getStatus().compareTo(experimentDAO.getStatus()) != 0) { + if (experimentDTO.getStatus() != null) { Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "Status is not editable."); throw new BadRequestException("Status is not editable."); } @@ -541,7 +553,7 @@ public class ExperimentService { finishExperiment(experimentDAO, endpoint); Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Finished the experiment: " + experimentDAO.toString()); }).start(); - experimentDTO = experimentDAO.convertToDTO(); + experimentDTO = experimentDAO.convertToDTO(true); return experimentDTO; } @@ -565,8 +577,8 @@ public class ExperimentService { Logging.LogExperimentAction(experimentDTO.getName(), experimentDTO.getUuid(), "Algorithm finished with code: " + code); // Results are stored in the experiment object - Map resultsDTO = ExperimentDAO.convertJsonStringToResult(String.valueOf(results)); - return new ExaremeResult(code, resultsDTO); + List<ExperimentDTO.ResultDTO> resultDTOS = JsonConverters.convertJsonStringToObject(String.valueOf(results), new ArrayList<ExperimentDTO.ResultDTO>().getClass()); + return new ExaremeResult(code, resultDTOS); } @@ -668,7 +680,7 @@ public class ExperimentService { Logging.LogUserAction(user.getUsername(), endpoint, "Run workflow completed!"); - experimentDTO = experimentDAO.convertToDTO(); + experimentDTO = experimentDAO.convertToDTO(true); return experimentDTO; } @@ -974,9 +986,9 @@ public class ExperimentService { static class ExaremeResult { private int code; - private Map results; + private List<ExperimentDTO.ResultDTO> results; - public ExaremeResult(int code, Map results) { + public ExaremeResult(int code, List<ExperimentDTO.ResultDTO> results) { this.code = code; this.results = results; } @@ -985,7 +997,7 @@ public class ExperimentService { return code; } - public Map getResults() { + public List<ExperimentDTO.ResultDTO> getResults() { return results; } } diff --git a/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java b/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java index 45169c09d7d14594259745dc479ff39b1db4d599..cfc6a576900041ab537b704dd0f9ed6527e30224 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java @@ -1,13 +1,11 @@ package eu.hbp.mip.services; import eu.hbp.mip.models.DAOs.ExperimentDAO; +import eu.hbp.mip.models.DAOs.UserDAO; import eu.hbp.mip.utils.Exceptions.BadRequestException; import org.springframework.data.jpa.domain.Specification; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; +import javax.persistence.criteria.*; import java.util.ArrayList; import java.util.List; @@ -82,6 +80,39 @@ public class ExperimentSpecifications { } } + public static class MyExperiment implements Specification<ExperimentDAO> { + + private String username; + + public MyExperiment(String username) { + this.username = username; + } + + public Predicate toPredicate(Root<ExperimentDAO> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) { + if (username == null) { + return cb.isTrue(cb.literal(true)); + } + Join<ExperimentDAO, UserDAO> experimentDAOUserDAOJoin = root.join("createdBy"); + return cb.equal(experimentDAOUserDAOJoin.get("username"), username); + } + } + + public static class SharedExperiment implements Specification<ExperimentDAO> { + + private boolean shared; + + public SharedExperiment(boolean shared) { + this.shared = shared; + } + + public Predicate toPredicate(Root<ExperimentDAO> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) { + if (shared == false) { + return cb.isTrue(cb.literal(false)); + } + return cb.equal(root.get("shared"), shared); + } + } + public static class ExperimentOrderBy implements Specification<ExperimentDAO> { private String orderBy; diff --git a/src/main/java/eu/hbp/mip/utils/ClaimUtils.java b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java index 4e4f95c9d25620c07b8884709e691e56c6894927..5eb90a2d076b68c2e871d054890239ccbd019907 100644 --- a/src/main/java/eu/hbp/mip/utils/ClaimUtils.java +++ b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java @@ -3,6 +3,7 @@ package eu.hbp.mip.utils; import com.google.gson.Gson; import eu.hbp.mip.models.DTOs.PathologyDTO; import eu.hbp.mip.utils.Exceptions.BadRequestException; +import eu.hbp.mip.utils.Exceptions.UnauthorizedException; import org.springframework.security.core.GrantedAuthority; import java.util.ArrayList; @@ -24,11 +25,11 @@ public class ClaimUtils { } public static void validateAccessRightsOnDatasets(String username, Collection<? extends GrantedAuthority> authorities, - String experimentDatasets) { + String experimentDatasets, String endpoint) { List<String> userClaims = Arrays.asList(authorities.toString().toLowerCase() .replaceAll("[\\s+\\]\\[]", "").split(",")); - Logging.LogUserAction(username, "(POST) /experiments/runAlgorithm", userClaims.toString()); + Logging.LogUserAction(username, endpoint, userClaims.toString()); // Don't check for dataset claims if "super" claim exists allowing everything if (!userClaims.contains(ClaimUtils.allDatasetsAllowedClaim())) { @@ -36,12 +37,12 @@ public class ClaimUtils { for (String dataset : experimentDatasets.split(",")) { String datasetRole = ClaimUtils.getDatasetClaim(dataset); if (!userClaims.contains(datasetRole.toLowerCase())) { - Logging.LogUserAction(username, "(POST) /experiments/runAlgorithm", + Logging.LogUserAction(username, endpoint, "You are not allowed to use dataset: " + dataset); - throw new BadRequestException("You are not authorized to use these datasets."); + throw new UnauthorizedException("You are not authorized to use these datasets."); } } - Logging.LogUserAction(username, "(POST) /experiments/runAlgorithm", + Logging.LogUserAction(username, endpoint, "User is authorized to use the datasets: " + experimentDatasets); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 30c82a447c62125c69a90159870e5a5fc8c6cc54..366f1c340ea0802001df9493415072ef41fb1376 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -54,7 +54,7 @@ services: ### Authentication ### authentication: - enabled: true + enabled: false ### Keycloak ### keycloak: diff --git a/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql b/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql index 5e1bcc6fb1e7ef9931db031241ff39097306069c..a1c728e963e8e510dfe6df4b128d19d6ce470922 100644 --- a/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql +++ b/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql @@ -33,12 +33,13 @@ DROP COLUMN password, DROP COLUMN phone, DROP COLUMN picture, DROP COLUMN team, -DROP COLUMN web; +DROP COLUMN web, +DROP COLUMN apikey; DROP TABLE "config_title", "config_yaxisvariables"; DROP TABLE "dataset_variable", "dataset_grouping", "dataset_data", "dataset_header"; DROP TABLE "query_variable", "query_grouping", "query_filter", "query_covariable"; -DROP TABLE "article_tag", "tag"; +DROP TABLE "article_tag", "tag","article"; DROP TABLE "variable_value"; DROP TABLE "query_training_datasets", "query_validation_datasets", "query_testing_datasets"; DROP TABLE "variable", "value";