From cf48ecb9f2f0c05dbff754a2205999927e7ea9ce Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Mon, 19 Oct 2020 02:21:46 -0700 Subject: [PATCH 01/52] Experiments/Models Refactoring 1) Removed Models entity. 2) CRUD for experiments. 3) Database cleanup. 4) Useless entities removed. 5) Implemented service layer. 6) DAOs - DTOs cleanup. --- build.sh | 2 +- pom.xml | 7 - .../eu/hbp/mip/controllers/AlgorithmsApi.java | 2 +- .../eu/hbp/mip/controllers/ArticlesApi.java | 193 ----- .../eu/hbp/mip/controllers/ExperimentApi.java | 818 +----------------- .../java/eu/hbp/mip/controllers/FilesAPI.java | 47 - .../eu/hbp/mip/controllers/MiningApi.java | 110 --- .../eu/hbp/mip/controllers/ModelsApi.java | 259 ------ .../hbp/mip/controllers/PathologiesApi.java | 2 +- .../java/eu/hbp/mip/controllers/StatsApi.java | 50 -- src/main/java/eu/hbp/mip/model/Article.java | 13 - src/main/java/eu/hbp/mip/model/Config.java | 112 --- .../eu/hbp/mip/model/DAOs/ExperimentDAO.java | 203 +++++ .../mip/model/{ => DTOs}/AlgorithmDTO.java | 2 +- .../eu/hbp/mip/model/DTOs/ExperimentDTO.java | 126 +++ .../mip/model/{ => DTOs}/PathologyDTO.java | 2 +- src/main/java/eu/hbp/mip/model/Dataset.java | 106 --- .../eu/hbp/mip/model/DatasetDescription.java | 46 - .../java/eu/hbp/mip/model/Experiment.java | 232 ----- .../hbp/mip/model/ExperimentExecutionDTO.java | 106 --- src/main/java/eu/hbp/mip/model/Group.java | 37 - src/main/java/eu/hbp/mip/model/Mining.java | 64 -- .../java/eu/hbp/mip/model/MiningQuery.java | 96 -- src/main/java/eu/hbp/mip/model/Model.java | 163 ---- src/main/java/eu/hbp/mip/model/Query.java | 164 ---- src/main/java/eu/hbp/mip/model/Tag.java | 39 - src/main/java/eu/hbp/mip/model/User.java | 228 +---- src/main/java/eu/hbp/mip/model/UserInfo.java | 1 - src/main/java/eu/hbp/mip/model/Value.java | 43 - src/main/java/eu/hbp/mip/model/Variable.java | 59 -- .../eu/hbp/mip/model/galaxy/WorkflowDTO.java | 2 +- .../mip/repositories/ConfigRepository.java | 11 - .../mip/repositories/DatasetRepository.java | 11 - .../repositories/ExperimentRepository.java | 8 +- .../hbp/mip/repositories/ModelRepository.java | 17 - .../hbp/mip/repositories/QueryRepository.java | 11 - .../hbp/mip/repositories/TagRepository.java | 11 - .../hbp/mip/repositories/ValueRepository.java | 11 - .../mip/repositories/VariableRepository.java | 10 - .../hbp/mip/services/ExperimentService.java | 776 +++++++++++++++++ .../java/eu/hbp/mip/utils/ClaimUtils.java | 2 +- .../java/eu/hbp/mip/utils/JsonConverters.java | 27 + .../V7_0__NewExperimentStructure.sql | 45 + src/main/resources/logback.xml | 3 +- 44 files changed, 1218 insertions(+), 3059 deletions(-) delete mode 100644 src/main/java/eu/hbp/mip/controllers/ArticlesApi.java delete mode 100644 src/main/java/eu/hbp/mip/controllers/FilesAPI.java delete mode 100644 src/main/java/eu/hbp/mip/controllers/MiningApi.java delete mode 100644 src/main/java/eu/hbp/mip/controllers/ModelsApi.java delete mode 100644 src/main/java/eu/hbp/mip/controllers/StatsApi.java delete mode 100644 src/main/java/eu/hbp/mip/model/Config.java create mode 100644 src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java rename src/main/java/eu/hbp/mip/model/{ => DTOs}/AlgorithmDTO.java (95%) create mode 100644 src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java rename src/main/java/eu/hbp/mip/model/{ => DTOs}/PathologyDTO.java (93%) delete mode 100644 src/main/java/eu/hbp/mip/model/Dataset.java delete mode 100644 src/main/java/eu/hbp/mip/model/DatasetDescription.java delete mode 100644 src/main/java/eu/hbp/mip/model/Experiment.java delete mode 100644 src/main/java/eu/hbp/mip/model/ExperimentExecutionDTO.java delete mode 100644 src/main/java/eu/hbp/mip/model/Group.java delete mode 100644 src/main/java/eu/hbp/mip/model/Mining.java delete mode 100644 src/main/java/eu/hbp/mip/model/MiningQuery.java delete mode 100644 src/main/java/eu/hbp/mip/model/Model.java delete mode 100644 src/main/java/eu/hbp/mip/model/Query.java delete mode 100644 src/main/java/eu/hbp/mip/model/Tag.java delete mode 100644 src/main/java/eu/hbp/mip/model/Value.java delete mode 100644 src/main/java/eu/hbp/mip/model/Variable.java delete mode 100644 src/main/java/eu/hbp/mip/repositories/ConfigRepository.java delete mode 100644 src/main/java/eu/hbp/mip/repositories/DatasetRepository.java delete mode 100644 src/main/java/eu/hbp/mip/repositories/ModelRepository.java delete mode 100644 src/main/java/eu/hbp/mip/repositories/QueryRepository.java delete mode 100644 src/main/java/eu/hbp/mip/repositories/TagRepository.java delete mode 100644 src/main/java/eu/hbp/mip/repositories/ValueRepository.java delete mode 100644 src/main/java/eu/hbp/mip/repositories/VariableRepository.java create mode 100644 src/main/java/eu/hbp/mip/services/ExperimentService.java create mode 100644 src/main/java/eu/hbp/mip/utils/JsonConverters.java create mode 100644 src/main/resources/db/migration/V7_0__NewExperimentStructure.sql diff --git a/build.sh b/build.sh index ef4f10565..6a18d98ec 100755 --- a/build.sh +++ b/build.sh @@ -26,7 +26,7 @@ else DOCKER="sudo docker" fi -IMAGE="hbpmip/portal-backend" +IMAGE="kfilippopolitis/portal-backend" VCS_REF=$(git describe --tags --dirty) VERSION=$(git describe --tags --dirty) diff --git a/pom.xml b/pom.xml index 20a6f0661..87ef337aa 100644 --- a/pom.xml +++ b/pom.xml @@ -211,13 +211,6 @@ <artifactId>jaxb-runtime</artifactId> <version>2.4.0-b180830.0438</version> </dependency> - - <dependency> - <groupId>com.auth0</groupId> - <artifactId>java-jwt</artifactId> - <version>3.8.3</version> - </dependency> - <!-- https://mvnrepository.com/artifact/com.github.jmchilton.blend4j/blend4j --> <dependency> <groupId>com.github.jmchilton.blend4j</groupId> diff --git a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java index 8310973a3..fcde558b8 100644 --- a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java +++ b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java @@ -8,7 +8,7 @@ import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import eu.hbp.mip.controllers.galaxy.retrofit.RetroFitGalaxyClients; import eu.hbp.mip.controllers.galaxy.retrofit.RetrofitClientInstance; -import eu.hbp.mip.model.AlgorithmDTO; +import eu.hbp.mip.model.DTOs.AlgorithmDTO; import eu.hbp.mip.model.UserInfo; import eu.hbp.mip.model.galaxy.WorkflowDTO; import eu.hbp.mip.utils.CustomResourceLoader; diff --git a/src/main/java/eu/hbp/mip/controllers/ArticlesApi.java b/src/main/java/eu/hbp/mip/controllers/ArticlesApi.java deleted file mode 100644 index d4eb46e9f..000000000 --- a/src/main/java/eu/hbp/mip/controllers/ArticlesApi.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Created by mirco on 04.12.15. - */ - -package eu.hbp.mip.controllers; - - -import com.github.slugify.Slugify; -import eu.hbp.mip.model.Article; -import eu.hbp.mip.model.User; -import eu.hbp.mip.model.UserInfo; -import eu.hbp.mip.repositories.ArticleRepository; -import eu.hbp.mip.utils.Logging; -import io.swagger.annotations.*; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import javax.validation.Valid; -import java.io.IOException; -import java.util.Date; -import java.util.Iterator; - -import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; - -@RestController -@RequestMapping(value = "/articles", produces = {APPLICATION_JSON_VALUE}) -@Api(value = "/articles", description = "the articles API") -public class ArticlesApi { - - @Autowired - private UserInfo userInfo; - - @Autowired - private ArticleRepository articleRepository; - - @ApiOperation(value = "Get articles", response = Article.class, responseContainer = "List") - @RequestMapping(method = RequestMethod.GET) - public ResponseEntity<Iterable> getArticles( - @ApiParam(value = "Only ask own articles") @RequestParam(value = "own", required = false) Boolean own, - @ApiParam(value = "Only ask results matching status", allowableValues = "draft, published") @RequestParam(value = "status", required = false) String status - ) { - User user = userInfo.getUser(); - Iterable<Article> articles; - Logging.LogUserAction(user.getUsername(), "(GET) /articles", "Loading articles..."); - - if (own != null && own) { - articles = articleRepository.findByCreatedBy(user); - } else { - articles = articleRepository.findByStatusOrCreatedBy("published", user); - } - int articlesSize = 0; - if (status != null) { - for (Iterator<Article> i = articles.iterator(); i.hasNext(); ) { - Article a = i.next(); - if (!status.equals(a.getStatus())) { - i.remove(); - } - articlesSize++; - } - } - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /articles", "Successfully Loaded " + articlesSize + " articles"); - - return ResponseEntity.ok(articles); - } - - - @ApiOperation(value = "Create an article") - @ApiResponses(value = {@ApiResponse(code = 201, message = "Article created")}) - @RequestMapping(method = RequestMethod.POST) - public ResponseEntity<Void> addAnArticle( - @RequestBody @ApiParam(value = "Article to create", required = true) @Valid Article article - ) { - - User user = userInfo.getUser(); - Logging.LogUserAction(user.getUsername(), "(POST) /articles", "Creating article..."); - - article.setCreatedAt(new Date()); - if ("published".equals(article.getStatus())) { - article.setPublishedAt(new Date()); - } - article.setCreatedBy(user); - - long count = 1; - for (int i = 1; count > 0; i++) { - count = articleRepository.countByTitle(article.getTitle()); - - if (count > 0) { - String title = article.getTitle(); - if (i > 1) { - title = title.substring(0, title.length() - 4); - } - article.setTitle(title + " (" + i + ")"); - } - } - - String slug; - try { - slug = new Slugify().slugify(article.getTitle()); - } catch (IOException e) { - slug = ""; - //LOGGER.trace("Cannot slugify title", e); - } - - boolean alreadyExists = true; - for (int i = 1; alreadyExists; i++) { - alreadyExists = articleRepository.exists(slug); - if (alreadyExists) { - if (i > 1) { - slug = slug.substring(0, slug.length() - 2); - } - slug += "-" + i; - } - article.setSlug(slug); - } - articleRepository.save(article); - - Logging.LogUserAction(user.getUsername(), "(POST) /articles", "Successfully created article with id : " + article.getSlug()); - return new ResponseEntity<>(HttpStatus.CREATED); - } - - - @ApiOperation(value = "Get an article", response = Article.class) - @RequestMapping(value = "/{slug}", method = RequestMethod.GET) - public ResponseEntity<Article> getAnArticle( - @ApiParam(value = "slug", required = true) @PathVariable("slug") String slug - ) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /articles/{slug}", "Loading article with id : " + slug); - - User user = userInfo.getUser(); - Article article; - article = articleRepository.findOne(slug); - - if (article == null) { - //LOGGER.warn("Cannot find article : " + slug); - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /articles/{slug}", "Article not found"); - return ResponseEntity.badRequest().body(null); - } - - if (!"published".equals(article.getStatus()) && !article.getCreatedBy().getUsername().equals(user.getUsername())) { - return new ResponseEntity<>(HttpStatus.FORBIDDEN); - } - - Logging.LogUserAction(user.getUsername(), "(GET) /articles/{slug}", "Successfully Loaded article with id : " + slug); - - return ResponseEntity.ok(article); - } - - - @ApiOperation(value = "Update an article") - @ApiResponses(value = {@ApiResponse(code = 204, message = "Article updated")}) - @RequestMapping(value = "/{slug}", method = RequestMethod.PUT) - public ResponseEntity<Void> updateAnArticle( - @ApiParam(value = "slug", required = true) @PathVariable("slug") String slug, - @RequestBody @ApiParam(value = "Article to update", required = true) @Valid Article article - ) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(PUT) /articles/{slug}", "Updating article with id : " + slug); - - User user = userInfo.getUser(); - - String author = articleRepository.findOne(slug).getCreatedBy().getUsername(); - - if (!user.getUsername().equals(author)) { - return new ResponseEntity<>(HttpStatus.FORBIDDEN); - } - - String oldTitle = articleRepository.findOne(slug).getTitle(); - - String newTitle = article.getTitle(); - - if (!newTitle.equals(oldTitle)) { - long count = 1; - for (int i = 1; count > 0 && !newTitle.equals(oldTitle); i++) { - newTitle = article.getTitle(); - count = articleRepository.countByTitle(newTitle); - if (count > 0 && !newTitle.equals(oldTitle)) { - if (i > 1) { - newTitle = newTitle.substring(0, newTitle.length() - 4); - } - article.setTitle(newTitle + " (" + i + ")"); - } - } - } - - articleRepository.save(article); - - Logging.LogUserAction(userInfo.getUser().getUsername(), "(PUT) /articles/{slug}", "Successfully pdated article with id : " + slug); - - return new ResponseEntity<>(HttpStatus.NO_CONTENT); - } - -} diff --git a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java index f05151474..d28d8aac3 100644 --- a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java +++ b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java @@ -1,43 +1,20 @@ package eu.hbp.mip.controllers; -import com.github.jmchilton.blend4j.galaxy.GalaxyInstance; -import com.github.jmchilton.blend4j.galaxy.GalaxyInstanceFactory; -import com.github.jmchilton.blend4j.galaxy.WorkflowsClient; -import com.github.jmchilton.blend4j.galaxy.beans.Workflow; -import com.github.jmchilton.blend4j.galaxy.beans.WorkflowDetails; -import com.github.jmchilton.blend4j.galaxy.beans.WorkflowInputDefinition; -import com.google.common.collect.Lists; -import com.google.gson.*; -import eu.hbp.mip.controllers.galaxy.retrofit.RetroFitGalaxyClients; -import eu.hbp.mip.controllers.galaxy.retrofit.RetrofitClientInstance; -import eu.hbp.mip.model.*; -import eu.hbp.mip.model.ExperimentExecutionDTO.AlgorithmExecutionDTO.AlgorithmExecutionParamDTO; -import eu.hbp.mip.model.galaxy.ErrorResponse; -import eu.hbp.mip.model.galaxy.GalaxyWorkflowResult; -import eu.hbp.mip.model.galaxy.PostWorkflowToGalaxyDtoResponse; -import eu.hbp.mip.repositories.ExperimentRepository; -import eu.hbp.mip.repositories.ModelRepository; -import eu.hbp.mip.utils.ClaimUtils; -import eu.hbp.mip.utils.HTTPUtil; -import eu.hbp.mip.utils.Logging; +import com.google.gson.Gson; +import eu.hbp.mip.model.DTOs.ExperimentDTO; +import eu.hbp.mip.model.UserInfo; +import eu.hbp.mip.services.ExperimentService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; -import org.codehaus.jettison.json.JSONException; -import org.codehaus.jettison.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; -import retrofit2.Call; -import retrofit2.Response; -import java.io.IOException; -import java.util.*; +import java.util.List; -import static java.lang.Thread.sleep; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; /* @@ -50,784 +27,41 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @Api(value = "/experiments") public class ExperimentApi { - private static final Gson gson = new Gson(); - @Autowired private UserInfo userInfo; - private static final Gson gsonOnlyExposed = new GsonBuilder().serializeNulls() - .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").excludeFieldsWithoutExposeAnnotation().create(); - - @Value("#{'${services.exareme.queryExaremeUrl}'}") - private String queryExaremeUrl; - - @Value("#{'${services.workflows.workflowUrl}'}") - private String workflowUrl; - - @Value("#{'${services.workflows.jwtSecret}'}") - private String jwtSecret; - - @Value("#{'${services.galaxy.galaxyUrl}'}") - private String galaxyUrl; - - @Value("#{'${services.galaxy.galaxyApiKey}'}") - private String galaxyApiKey; - - // Enable HBP collab authentication (1) or disable it (0). Default is 1 - @Value("#{'${hbp.authentication.enabled:1}'}") - private boolean authenticationIsEnabled; - @Autowired - private ModelRepository modelRepository; + private ExperimentService experimentService; - @Autowired - private ExperimentRepository experimentRepository; + @ApiOperation(value = "Get experiments", response = ExperimentDTO.class, responseContainer = "List") + @RequestMapping(method = RequestMethod.GET) + public ResponseEntity<String> getExperiments() { + return experimentService.getExperiments("(GET) /experiments"); + } - @ApiOperation(value = "get an experiment", response = Experiment.class) + @ApiOperation(value = "Get an experiment", response = ExperimentDTO.class) @RequestMapping(value = "/{uuid}", method = RequestMethod.GET) public ResponseEntity<String> getExperiment( @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - - Experiment experiment; - UUID experimentUuid; - User user = userInfo.getUser(); - - Logging.LogUserAction(user.getUsername(), "(GET) /experiments/{uuid}", "Loading Experiment with uuid : " + uuid); - - try { - experimentUuid = UUID.fromString(uuid); - } catch (IllegalArgumentException iae) { - Logging.LogUserAction(user.getUsername(), "(GET) /experiments/{uuid}", "Invalid Experiment UUID."); - return ResponseEntity.badRequest().body("Invalid Experiment UUID"); - } - - experiment = experimentRepository.findOne(experimentUuid); - - if (experiment == null) { - Logging.LogUserAction(user.getUsername(), "(GET) /experiments/{uuid}", "Experiment Not found."); - return new ResponseEntity<>("Not found", HttpStatus.NOT_FOUND); - } - - if (!experiment.isShared() && !experiment.getCreatedBy().getUsername().equals(user.getUsername())) { - Logging.LogUserAction(user.getUsername(), "(GET) /experiments/{uuid}", "Accessing Experiment is unauthorized."); - return new ResponseEntity<>("You don't have access to the experiment.", HttpStatus.UNAUTHORIZED); - } - - Logging.LogUserAction(user.getUsername(), "(GET) /experiments/{uuid}", "Experiment was Loaded with uuid : " + uuid + "."); - - return new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); - } - - - @ApiOperation(value = "Create an experiment", response = Experiment.class) - @RequestMapping(value = "/runAlgorithm", method = RequestMethod.POST) - public ResponseEntity<String> runExperiment(Authentication authentication, @RequestBody ExperimentExecutionDTO experimentExecutionDTO) { - User user = userInfo.getUser(); - // Get the type and name of algorithm - String algorithmType = experimentExecutionDTO.getAlgorithms().get(0).getType(); - String algorithmName = experimentExecutionDTO.getAlgorithms().get(0).getName(); - - StringBuilder parametersLogMessage = new StringBuilder(", Parameters:\n"); - for (ExperimentExecutionDTO.AlgorithmExecutionDTO.AlgorithmExecutionParamDTO params : experimentExecutionDTO.getAlgorithms().get(0).getParameters()) { - parametersLogMessage - .append(" ") - .append(params.getLabel()) - .append(" -> ") - .append(params.getValue()) - .append("\n"); - } - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", "Executing " + algorithmName + parametersLogMessage); - - if (authenticationIsEnabled) { - // Getting the dataset from the experiment parameters - String experimentDatasets = null; - for (ExperimentExecutionDTO.AlgorithmExecutionDTO.AlgorithmExecutionParamDTO parameter : experimentExecutionDTO.getAlgorithms().get(0).getParameters()) { - if (parameter.getLabel().equals("dataset")) { - experimentDatasets = parameter.getValue(); - break; - } - } - - if (experimentDatasets == null || experimentDatasets.equals("")) { - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", - "A dataset should be specified to run an algorithm."); - return ResponseEntity.badRequest().body("Please provide at least one dataset to run the algorithm."); - } - - // --- Validating proper access rights on the datasets --- - if (!ClaimUtils.userHasDatasetsAuthorization(user.getUsername(), authentication.getAuthorities(), experimentDatasets)) { - return ResponseEntity.badRequest().body("You are not authorized to use these datasets."); - } - } - - // Run with the appropriate engine - if (algorithmType.equals("workflow")) { - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", "Algorithm runs on Galaxy."); - return runGalaxyWorkflow(experimentExecutionDTO); - } else { - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", "Algorithm runs on Exareme."); - return runExaremeAlgorithm(experimentExecutionDTO); - } + return experimentService.getExperiment(uuid, "(GET) /experiments/{uuid}"); } - @ApiOperation(value = "Mark an experiment as viewed", response = Experiment.class) - @RequestMapping(value = "/{uuid}/markAsViewed", method = RequestMethod.GET) - public ResponseEntity<String> markExperimentAsViewed( - @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - - Experiment experiment; - UUID experimentUuid; - User user = userInfo.getUser(); - - Logging.LogUserAction(user.getUsername(), "(GET) /experiments/{uuid}/markAsViewed", "Marking as viewed the experiment with uuid : " + uuid + "."); - - try { - experimentUuid = UUID.fromString(uuid); - } catch (IllegalArgumentException iae) { - Logging.LogUserAction(user.getUsername(), "(GET) /experiments/{uuid}/markAsViewed", "Invalid Experiment UUID" + uuid + "."); - return ResponseEntity.badRequest().body("Invalid Experiment UUID"); - } - - experiment = experimentRepository.findOne(experimentUuid); - if (!experiment.getCreatedBy().getUsername().equals(user.getUsername())) { - Logging.LogUserAction(user.getUsername(), "(GET) /experiments/{uuid}/markAsViewed", "You're not the owner of this experiment"); - return new ResponseEntity<>("You're not the owner of this experiment", HttpStatus.UNAUTHORIZED); - } - experiment.setResultsViewed(true); - experimentRepository.save(experiment); - - Logging.LogUserAction(user.getUsername(), "(GET) /experiments/{uuid}/markAsViewed", "Experiment with uuid: " + uuid + " was marked as viewed."); - - return new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); + @ApiOperation(value = "Create an experiment", response = ExperimentDTO.class) + @RequestMapping(method = RequestMethod.POST) + public ResponseEntity<String> createExperiment(Authentication authentication, @RequestBody ExperimentDTO experimentDTO) { + return experimentService.createExperiment(authentication, experimentDTO, "(POST) /experiments"); } - @ApiOperation(value = "Mark an experiment as shared", response = Experiment.class) - @RequestMapping(value = "/{uuid}/markAsShared", method = RequestMethod.GET) - public ResponseEntity<String> markExperimentAsShared( - @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /experiments/{uuid}/markAsShared", "Marking as shared the experiment with uuid : " + uuid + "."); - - return doMarkExperimentAsShared(uuid, true); + @ApiOperation(value = "Update an experiment", response = ExperimentDTO.class) + @RequestMapping(value = "/{uuid}", method = RequestMethod.PATCH) + public ResponseEntity<String> updateExperiment(@RequestBody ExperimentDTO experimentDTO,@ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { + return experimentService.updateExperiment(uuid, experimentDTO, "(PATCH) /experiments/{uuid}"); } - @ApiOperation(value = "Mark an experiment as unshared", response = Experiment.class) - @RequestMapping(value = "/{uuid}/markAsUnshared", method = RequestMethod.GET) - public ResponseEntity<String> markExperimentAsUnshared( + @ApiOperation(value = "Delete an experiment", response = boolean.class) + @RequestMapping(value = "/{uuid}", method = RequestMethod.DELETE) + public ResponseEntity<String> deleteExperiment( @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /experiments/{uuid}/markAs/Unshared", "Marking as unshared the experiment with uuid : " + uuid + "."); - - return doMarkExperimentAsShared(uuid, false); - } - - @ApiOperation(value = "list experiments", response = Experiment.class, responseContainer = "List") - @RequestMapping(method = RequestMethod.GET, params = {"maxResultCount"}) - public ResponseEntity<String> listExperiments( - @ApiParam(value = "maxResultCount") @RequestParam int maxResultCount) { - User user = userInfo.getUser(); - Logging.LogUserAction(user.getUsername(), "(GET) /experiments/{maxResultCount}", "Listing experiments with a maximum amount of : " + maxResultCount + "."); - - List<Experiment> expList = doListExperiments(false, null); - Logging.LogUserAction(user.getUsername(), "(GET) /experiments/{maxResultCount}", "Successfully listed experiments."); - - return new ResponseEntity<>(gsonOnlyExposed.toJson(expList), HttpStatus.OK); - } - - @ApiOperation(value = "list experiments", response = Experiment.class, responseContainer = "List") - @RequestMapping(method = RequestMethod.GET, params = {"slug", "maxResultCount"}) - public ResponseEntity<String> listExperiments(@ApiParam(value = "slug") @RequestParam("slug") String modelSlug, - @ApiParam(value = "maxResultCount") @RequestParam("maxResultCount") int maxResultCount) { - User user = userInfo.getUser(); - Logging.LogUserAction(user.getUsername(), "(GET) /experiments/{slug}/{maxResultCount}", "Listing experiments with a maximum amount of :" + maxResultCount + "with modelSlug : " + modelSlug + "."); - - if (maxResultCount <= 0 || modelSlug == null || "".equals(modelSlug)) { - return new ResponseEntity<>("You must provide at least a slug or a limit of result", - HttpStatus.BAD_REQUEST); - } - - List<Experiment> expList = doListExperiments(false, null); - Logging.LogUserAction(user.getUsername(), "(GET) /experiments/{slug}/{maxResultCount}", "Successfully listed my experiments."); - return new ResponseEntity<>(gsonOnlyExposed.toJson(expList), HttpStatus.OK); - } - - @ApiOperation(value = "list my experiments", response = Experiment.class, responseContainer = "List") - @RequestMapping(method = RequestMethod.GET, params = {"mine"}) - public ResponseEntity<String> listMyExperiments(Authentication authentication, @ApiParam(value = "mine") @RequestParam("mine") boolean mine) { - User user = userInfo.getUser(); - Logging.LogUserAction(user.getUsername(), "(GET) /experiments/{mine}", "Listing my experiments."); - List<Experiment> expList = doListExperiments(true, null); - Logging.LogUserAction(user.getUsername(), "(GET) /experiments/{mine}", "Successfully listed my experiments."); - return new ResponseEntity<>(gsonOnlyExposed.toJson(expList), HttpStatus.OK); - } - - private List<Experiment> doListExperiments(boolean mine, String modelSlug) { - User user = userInfo.getUser(); - - Iterable<Experiment> myExperiments = experimentRepository.findByCreatedBy(user); - List<Experiment> expList = Lists.newLinkedList(myExperiments); - if (!mine) { - Iterable<Experiment> sharedExperiments = experimentRepository.findByShared(true); - List<Experiment> sharedExpList = Lists.newLinkedList(sharedExperiments); - expList.addAll(sharedExpList); - } - - if (modelSlug != null && !"".equals(modelSlug)) { - for (Iterator<Experiment> it = expList.iterator(); it.hasNext(); ) { - Experiment e = it.next(); - e.setResult(null); - e.setAlgorithms(null); - e.setValidations(null); - if (!e.getModel().getSlug().equals(modelSlug)) { - it.remove(); - } - } - } - return expList; - } - - private ResponseEntity<String> doMarkExperimentAsShared(String uuid, boolean shared) { - Experiment experiment; - UUID experimentUuid; - User user = userInfo.getUser(); - - try { - experimentUuid = UUID.fromString(uuid); - } catch (IllegalArgumentException iae) { - //LOGGER.trace("Invalid UUID", iae); - //LOGGER.warn("An invalid Experiment UUID was received !"); - Logging.LogUserAction(user.getUsername(), "List my experiments", "Listing my experiments."); - return ResponseEntity.badRequest().body("Invalid Experiment UUID"); - } - - experiment = experimentRepository.findOne(experimentUuid); - - if (!experiment.getCreatedBy().getUsername().equals(user.getUsername())) - return new ResponseEntity<>("You're not the owner of this experiment", HttpStatus.UNAUTHORIZED); - - experiment.setShared(shared); - experimentRepository.save(experiment); - - Logging.LogUserAction(user.getUsername(), "Experiment updated (marked as shared)", ""); - - return new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); - } - - /* ------------------------------- EXPERIMENT MODEL METHODS ----------------------------------------------------*/ - - public Experiment createExperiment(ExperimentExecutionDTO experimentExecutionDTO) { - User user = userInfo.getUser(); - - Experiment experiment = new Experiment(); - experiment.setUuid(UUID.randomUUID()); - experiment.setCreatedBy(user); - experiment.setAlgorithms(gson.toJson(experimentExecutionDTO.getAlgorithms())); - Model model = modelRepository.findOne(experimentExecutionDTO.getModel()); - experiment.setModel(model); - experiment.setName(experimentExecutionDTO.getName()); - experimentRepository.save(experiment); - - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", " id : " + experiment.getUuid()); - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", " algorithms : " + experiment.getAlgorithms()); - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", " model : " + experiment.getModel().getSlug()); - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", " name : " + experiment.getName()); - return experiment; - } - - private void saveExperiment(Experiment experiment) { - User user = userInfo.getUser(); - - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", " id : " + experiment.getUuid()); - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", " algorithms : " + experiment.getAlgorithms()); - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", " model : " + experiment.getModel().getSlug()); - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", " name : " + experiment.getName()); - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", " historyId : " + experiment.getWorkflowHistoryId()); - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", " status : " + experiment.getWorkflowStatus()); - - experimentRepository.save(experiment); - - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", "Saved experiment"); - } - - private void finishExperiment(Experiment experiment) { - experiment.setFinished(new Date()); - experimentRepository.save(experiment); - } - - /* -------------------------------------- EXAREME CALLS ---------------------------------------------------------*/ - - /** - * The runExaremeExperiment will POST the algorithm to the exareme client - * - * @param experimentExecutionDTO is the request with the experiment information - * @return the response to be returned - */ - public ResponseEntity<String> runExaremeAlgorithm(ExperimentExecutionDTO experimentExecutionDTO) { - User user = userInfo.getUser(); - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", "Running the algorithm..."); - - Experiment experiment = createExperiment(experimentExecutionDTO); - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", "Created experiment with uuid :" + experiment.getUuid()); - - // Run the 1st algorithm from the list - String algorithmName = experimentExecutionDTO.getAlgorithms().get(0).getName(); - - // Get the parameters - List<ExperimentExecutionDTO.AlgorithmExecutionDTO.AlgorithmExecutionParamDTO> algorithmParameters - = experimentExecutionDTO.getAlgorithms().get(0).getParameters(); - - String body = gson.toJson(algorithmParameters); - String url = queryExaremeUrl + "/" + algorithmName; - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", "url: " + url + ", body: " + body); - - ResponseEntity<String> response = new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", - "Completed, returning: " + experiment.toString()); - - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", - "Starting exareme execution thread"); - new Thread(() -> { - // ATTENTION: Inside the Thread only LogExperimentAction should be used, not LogUserAction! - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "Thread named :" + Thread.currentThread().getName() + " with id :" + Thread.currentThread().getId() + " started!"); - - try { - StringBuilder results = new StringBuilder(); - int code = HTTPUtil.sendPost(url, body, results); - - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "Algorithm finished with code: " + code); - - // Results are stored in the experiment object - experiment.setResult("[" + results.toString() + "]"); - experiment.setHasError(code >= 400); - experiment.setHasServerError(code >= 500); - } catch (Exception e) { - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "There was an exception: " + e.getMessage()); - - experiment.setHasError(true); - experiment.setHasServerError(true); - experiment.setResult(e.getMessage()); - } - - finishExperiment(experiment); - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "Finished the experiment: " + experiment.toString()); - }).start(); - - return response; - } - - /* --------------------------------------- GALAXY CALLS ---------------------------------------------------------*/ - - - /** - * The runWorkflow will POST the algorithm to the galaxy client - * - * @param experimentExecutionDTO is the request with the experiment information - * @return the response to be returned - */ - public ResponseEntity<String> runGalaxyWorkflow(ExperimentExecutionDTO experimentExecutionDTO) { - User user = userInfo.getUser(); - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", "Running a workflow..."); - - Experiment experiment = createExperiment(experimentExecutionDTO); - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", "Created experiment with uuid :" + experiment.getUuid()); - - - // Run the 1st algorithm from the list - String workflowId = experimentExecutionDTO.getAlgorithms().get(0).getName(); - - // Get the parameters - List<AlgorithmExecutionParamDTO> algorithmParameters - = experimentExecutionDTO.getAlgorithms().get(0).getParameters(); - - // Convert the parameters to workflow parameters - HashMap<String, String> algorithmParamsIncludingEmpty = new HashMap<>(); - if (algorithmParameters != null) { - for (AlgorithmExecutionParamDTO param : algorithmParameters) { - algorithmParamsIncludingEmpty.put(param.getName(), param.getValue()); - } - } - - // Get all the algorithm parameters because the frontend provides only the non-null - final GalaxyInstance instance = GalaxyInstanceFactory.get(galaxyUrl, galaxyApiKey); - final WorkflowsClient workflowsClient = instance.getWorkflowsClient(); - Workflow workflow = null; - for (Workflow curWorkflow : workflowsClient.getWorkflows()) { - if (curWorkflow.getId().equals(workflowId)) { - workflow = curWorkflow; - break; - } - } - if (workflow == null) { - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", - "Could not find algorithm code: " + workflowId); - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body(new ErrorResponse("Could not find galaxy algorithm.").toString()); - } - final WorkflowDetails workflowDetails = workflowsClient.showWorkflow(workflow.getId()); - for (Map.Entry<String, WorkflowInputDefinition> workflowParameter : workflowDetails.getInputs().entrySet()) { - if (!(algorithmParamsIncludingEmpty.containsKey(workflowParameter.getValue().getUuid()))) { - algorithmParamsIncludingEmpty.put(workflowParameter.getValue().getUuid(), ""); - } - } - - // Create the body of the request - HashMap<String, HashMap<String, String>> requestBody = new HashMap<>(); - requestBody.put("inputs", algorithmParamsIncludingEmpty); - JsonObject requestBodyJson = new JsonParser().parse(gson.toJson(requestBody)).getAsJsonObject(); - - // Create the request client - RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", "Running Galaxy workflow with id: " + workflow.getId()); - - // Call Galaxy to run the workflow - Call<PostWorkflowToGalaxyDtoResponse> call = service.postWorkflowToGalaxy(workflow.getId(), galaxyApiKey, requestBodyJson); - try { - Response<PostWorkflowToGalaxyDtoResponse> response = call.execute(); - - if (response.code() == 200) { // Call succeeded - String responseBody = gson.toJson(response.body()); - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", "Response: " + responseBody); - - String historyId = (String) new JSONObject(responseBody).get("history_id"); - experiment.setWorkflowHistoryId(historyId); - experiment.setWorkflowStatus("running"); - experiment.setHasError(false); - experiment.setHasServerError(response.code() >= 500); - - } else { // Something unexpected happened - String msgErr = gson.toJson(response.errorBody()); - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", "Error Response: " + msgErr); - - // Values are read from streams. - JSONObject jObjectError = new JSONObject(msgErr); - String errMsg = jObjectError.get("err_msg").toString(); - - experiment.setResult("[" + new ErrorResponse(errMsg).toString() + "]"); - experiment.setHasError(response.code() >= 400); - experiment.setHasServerError(response.code() >= 500); - } - - } catch (Exception e) { - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", "An exception occurred: " + e.getMessage()); - experiment.setHasError(true); - experiment.setHasServerError(true); - experiment.setResult(e.getMessage()); - } - saveExperiment(experiment); - - // Start the process of fetching the status - updateWorkflowExperiment(experiment); - - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", "Run workflow completed!"); - - return new ResponseEntity(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); - } - - - /** - * This method creates a thread that will fetch the workflow result when it is ready - * - * @param experiment will be used to fetch it's workflow status, it should have the workflowHistoryId initialized - * and the result should not already be fetched - * @return nothing, just updates the experiment - */ - public void updateWorkflowExperiment(Experiment experiment) { - User user = userInfo.getUser(); - - if (experiment == null) { - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", "The experiment does not exist."); - return; - } - - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", - " Experiment id : " + experiment.getUuid()); - if (experiment.getWorkflowHistoryId() == null) { - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", "History Id does not exist."); - return; - } - - Logging.LogUserAction(user.getUsername(), "(POST) /experiments/runAlgorithm", "Starting Thread..."); - new Thread(() -> { - while (true) { - // ATTENTION: Inside the Thread only LogExperimentAction should be used, not LogExperimentAction! - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "Thread is running..."); - - try { - sleep(2000); - } catch (InterruptedException e) { - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "Sleep was disrupted: " + e.getMessage()); - } - - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "Fetching status for experiment Id: " + experiment.getUuid()); - - String state = getWorkflowStatus(experiment); - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "State is: " + state); - - switch (state) { - case "running": - // Do nothing, when the experiment is created the status is set to running - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "Workflow is still running."); - break; - - case "completed": - // Get only the job result that is visible - List<GalaxyWorkflowResult> workflowJobsResults = getWorkflowResults(experiment); - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "Results are: " + workflowJobsResults.toString()); - - boolean resultFound = false; - for (GalaxyWorkflowResult jobResult : workflowJobsResults) { - if (jobResult.getVisible()) { - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "Visible result are: " + jobResult.getId()); - - String result = getWorkflowResultBody(experiment, jobResult.getId()); - - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "Result: " + result); - if (result == null) { - experiment.setHasError(true); - experiment.setHasServerError(true); - } - experiment.setResult("[" + result + "]"); - experiment.setWorkflowStatus("completed"); - resultFound = true; - } - } - - if (!resultFound) { // If there is no visible result - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "No visible result"); - experiment.setResult("[" + new ErrorResponse("The workflow has no visible result.").toString() + "]"); - experiment.setHasError(true); - experiment.setHasServerError(true); - } - - finishExperiment(experiment); - break; - - case "error": - // Get the job result that failed - workflowJobsResults = getWorkflowResults(experiment); - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "Error results are: " + workflowJobsResults.toString()); - - boolean failedJobFound = false; - for (GalaxyWorkflowResult jobResult : workflowJobsResults) { - if (jobResult.getState().equals("error")) { - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "Failed job is: " + jobResult.getId()); - - String result = getWorkflowJobError(jobResult.getId(), experiment); - - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "Job result: " + result); - if (result == null) { - experiment.setHasError(true); - experiment.setHasServerError(true); - } - experiment.setResult("[" + result + "]"); - experiment.setWorkflowStatus("error"); - failedJobFound = true; - } - } - - if (!failedJobFound) { // If there is no visible failed job - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "No failed result"); - experiment.setResult("[" + new ErrorResponse("The workflow has no failed result.").toString() + "]"); - experiment.setHasError(true); - experiment.setHasServerError(true); - } - finishExperiment(experiment); - break; - - default: // InternalError or unexpected result - experiment.setResult("[" + new ErrorResponse("An unexpected error occurred.").toString() + "]"); - experiment.setHasError(true); - experiment.setHasServerError(true); - finishExperiment(experiment); - break; - } - - // If result exists return - if (experiment.getResult() != null) { - Logging.LogExperimentAction(experiment.getName(), experiment.getUuid(), "Result exists: " + experiment.getResult()); - return; - } - } - }).start(); - } - - - /** - * @param experiment The experiment of the workflow - * @return "running" -> When the workflow is still running - * "internalError" -> When an exception or a bad request occurred - * "error" -> When the workflow produced an error - * "completed" -> When the workflow completed successfully - */ - public String getWorkflowStatus(Experiment experiment) { - String historyId = experiment.getWorkflowHistoryId(); - String experimentName = experiment.getName(); - UUID experimentId = experiment.getUuid(); - - // ATTENTION: This function is used from a Thread. Only LogExperimentAction should be used, not LogUserAction! - Logging.LogExperimentAction(experimentName, experimentId, " History Id : " + historyId); - - // Create the request client - RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); - Call<Object> call = service.getWorkflowStatusFromGalaxy(historyId, galaxyApiKey); - - String result = null; - try { - Response<Object> response = call.execute(); - if (response.code() >= 400) { - Logging.LogExperimentAction(experimentName, experimentId, " Response code: " - + response.code() + "" + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); - return "internalError"; - } - result = new Gson().toJson(response.body()); - Logging.LogExperimentAction(experimentName, experimentId, " Result: " + result); - - } catch (IOException e) { - Logging.LogExperimentAction(experimentName, experimentId, " An exception happened: " + e.getMessage()); - return "internalError"; - } - - String state = null; - try { - JSONObject resultJson = new JSONObject(result); - state = resultJson.getString("state"); - } catch (JSONException e) { - Logging.LogExperimentAction(experimentName, experimentId, " An exception happened: " + e.getMessage()); - return "internalError"; - } - - Logging.LogExperimentAction(experimentName, experimentId, " Completed!"); - switch (state) { - case "ok": - return "completed"; - case "error": - return "error"; - case "running": - case "new": - case "waiting": - case "queued": - return "running"; - default: - return "internalError"; - } - } - - /** - * @param experiment The experiment of the workflow - * @return a List<GalaxyWorkflowResult> or null when an error occurred - */ - public List<GalaxyWorkflowResult> getWorkflowResults(Experiment experiment) { - - String historyId = experiment.getWorkflowHistoryId(); - String experimentName = experiment.getName(); - UUID experimentId = experiment.getUuid(); - Logging.LogExperimentAction(experimentName, experimentId, " historyId : " + historyId); - - RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); - Call<List<GalaxyWorkflowResult>> call = service.getWorkflowResultsFromGalaxy(historyId, galaxyApiKey); - - List<GalaxyWorkflowResult> getGalaxyWorkflowResultList = null; - try { - Response<List<GalaxyWorkflowResult>> response = call.execute(); - if (response.code() >= 400) { - Logging.LogExperimentAction(experimentName, experimentId, " Response code: " - + response.code() + "" + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); - return null; - } - getGalaxyWorkflowResultList = response.body(); - Logging.LogExperimentAction(experimentName, experimentId, " Result: " + response.body()); - - } catch (IOException e) { - Logging.LogExperimentAction(experimentName, experimentId, " An exception happened: " + e.getMessage()); - return null; - } - - Logging.LogExperimentAction(experimentName, experimentId, " Completed!"); - return getGalaxyWorkflowResultList; - + return experimentService.deleteExperiment(uuid, "(DELETE) /experiments/{uuid}"); } - - /** - * @param experiment The experiment of the workflow - * @param contentId the id of the job result that we want - * @return the result of the specific workflow job, null if there was an error - */ - public String getWorkflowResultBody(Experiment experiment, String contentId) { - - String historyId = experiment.getWorkflowHistoryId(); - String experimentName = experiment.getName(); - UUID experimentId = experiment.getUuid(); - - Logging.LogExperimentAction(experimentName, experimentId, " historyId : " + historyId); - - RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); - Call<Object> call = - service.getWorkflowResultsBodyFromGalaxy(historyId, contentId, galaxyApiKey); - - String resultJson = null; - try { - Response<Object> response = call.execute(); - if (response.code() >= 400) { - Logging.LogExperimentAction(experimentName, experimentId, " Response code: " - + response.code() + "" + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); - return null; - } - resultJson = new Gson().toJson(response.body()); - Logging.LogExperimentAction(experimentName, experimentId, " Result: " + resultJson); - - } catch (IOException e) { - Logging.LogExperimentAction(experimentName, experimentId, - " An exception happened: " + e.getMessage()); - return null; - } - - Logging.LogExperimentAction(experimentName, experimentId, " Completed!"); - return resultJson; - } - - - /** - * @param jobId the id of the workflow job that failed - * @return the error that was produced or null if an error occurred - */ - public String getWorkflowJobError(String jobId, Experiment experiment) { - String experimentName = experiment.getName(); - UUID experimentId = experiment.getUuid(); - - Logging.LogExperimentAction(experimentName, experimentId, " jobId : " + jobId); - RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); - Call<Object> callError = service.getErrorMessageOfWorkflowFromGalaxy(jobId, galaxyApiKey); - - String fullError = null; - String returnError = null; - try { - Response<Object> response = callError.execute(); - if (response.code() >= 400) { - Logging.LogExperimentAction(experimentName, experimentId, "Response code: " - + response.code() + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); - return null; - } - - // Parsing the stderr of the job that failed - String jsonString = new Gson().toJson(response.body()); - JsonElement jsonElement = new JsonParser().parse(jsonString); - JsonObject rootObject = jsonElement.getAsJsonObject(); - fullError = rootObject.get("stderr").getAsString(); - Logging.LogExperimentAction(experimentName, experimentId, "Error: " + fullError); - - String[] arrOfStr = fullError.split("ValueError", 0); - String specError = arrOfStr[arrOfStr.length - 1]; - returnError = specError.substring(1); - Logging.LogExperimentAction(experimentName, experimentId, "Parsed Error: " + returnError); - - } catch (IOException e) { - Logging.LogExperimentAction(experimentName, experimentId, "Exception: " + e.getMessage()); - return null; - } - - Logging.LogExperimentAction(experimentName, experimentId, "Completed successfully!"); - - return returnError; - } - - -} +} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/controllers/FilesAPI.java b/src/main/java/eu/hbp/mip/controllers/FilesAPI.java deleted file mode 100644 index 69e7f3ba9..000000000 --- a/src/main/java/eu/hbp/mip/controllers/FilesAPI.java +++ /dev/null @@ -1,47 +0,0 @@ -package eu.hbp.mip.controllers; - -import eu.hbp.mip.model.User; -import eu.hbp.mip.model.UserInfo; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; -import eu.hbp.mip.utils.Logging; - -/** - * Created by mirco on 08.05.17. - */ - -@RestController -@RequestMapping(value = "/protected") -@Api(value = "/protected", description = "the protected files API") -public class FilesAPI { - - - @Autowired - private UserInfo userInfo; - - @ApiOperation(value = "Get protected files") - @RequestMapping(value = "/{filename:.+}", method = RequestMethod.GET) - public ResponseEntity<Void> getProtectedFile( - @ApiParam(value = "filename", required = true) @PathVariable("filename") String filename - ) { - User user = userInfo.getUser(); - Logging.LogUserAction(user.getUsername(), "(GET) /protected/{filename:.+}", "Loading protected file with filename : " + filename); - - String filepath = "/protected/" + filename; - Logging.LogUserAction(user.getUsername(), "(GET) /protected/{filename:.+}" + filepath, "Downloaded protected file"); - - HttpHeaders headers = new HttpHeaders(); - headers.add("X-Accel-Redirect", filepath); - - return new ResponseEntity<>(headers, HttpStatus.OK); - } -} diff --git a/src/main/java/eu/hbp/mip/controllers/MiningApi.java b/src/main/java/eu/hbp/mip/controllers/MiningApi.java deleted file mode 100644 index 6a198cc0d..000000000 --- a/src/main/java/eu/hbp/mip/controllers/MiningApi.java +++ /dev/null @@ -1,110 +0,0 @@ -package eu.hbp.mip.controllers; - -import eu.hbp.mip.utils.HTTPUtil; - -import com.google.gson.Gson; - -import eu.hbp.mip.model.UserInfo; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; - -import eu.hbp.mip.utils.Logging; - - -import java.util.*; -import java.io.IOException; - -import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; - -/** - * Created by mirco on 06.01.17. - */ -@RestController -@RequestMapping(value = "/mining", produces = {APPLICATION_JSON_VALUE}) -@Api(value = "/mining", description = "the mining API") -public class MiningApi { - - private static final Gson gson = new Gson(); - - @Autowired - private UserInfo userInfo; - - @Value("#{'${services.exareme.queryExaremeUrl:http://localhost:9090/mining/query}'}") - public String queryExaremeUrl; - - @ApiOperation(value = "Create a histogram on Exareme", response = String.class) - @RequestMapping(value = "/histograms", method = RequestMethod.POST) - public ResponseEntity runExaremeHistograms(@RequestBody List<HashMap<String, String>> queryList) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(POST) /mining/histogram", "Executing histogram..."); - - String query = gson.toJson(queryList); - String url = queryExaremeUrl + "/" + "MULTIPLE_HISTOGRAMS"; - - try { - StringBuilder results = new StringBuilder(); - int code = HTTPUtil.sendPost(url, query, results); - - Logging.LogUserAction(userInfo.getUser().getUsername(), "(POST) /mining/histogram", "Executed histogram with result :" + results.toString()); - return ResponseEntity.ok(gson.toJson(results.toString())); - } catch (IOException e) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(POST) /mining/histogram", "Histogram algorithm was not found"); - return new ResponseEntity<>("Not found", HttpStatus.NOT_FOUND); - } - } - - @ApiOperation(value = "Create a descriptive statistic on Exareme", response = String.class) - @RequestMapping(value = "/descriptive_stats", method = RequestMethod.POST) - public ResponseEntity runExaremeDescriptiveStats(@RequestBody List<HashMap<String, String>> queryList) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(POST) /experiments/descriptive_stats", "Executing Exareme descriptive stats..."); - - String query = gson.toJson(queryList); - String url = queryExaremeUrl + "/" + "DESCRIPTIVE_STATS"; - - try { - StringBuilder results = new StringBuilder(); - int code = HTTPUtil.sendPost(url, query, results); - Logging.LogUserAction(userInfo.getUser().getUsername(), "(POST) /experiments/descriptive_stats", "Executed descriptive stats with result : " + results.toString()); - return ResponseEntity.ok(gson.toJson(results.toString())); - } catch (IOException e) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(POST) /experiments/descriptive_stats", "Descriptive stats algorithm was not found"); - return new ResponseEntity<>("Not found", HttpStatus.NOT_FOUND); - } - } - - @ApiOperation(value = "Create a descriptive statistic on Exareme", response = String.class) - @RequestMapping(value = "/descriptive_stats_v2", method = RequestMethod.POST) - public ResponseEntity runExaremeDescriptiveStatsV2(@RequestBody List<HashMap<String, String>> queryList) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(POST) /experiments/descriptive_stats_v2", "Executing an Exareme descriptive stats v2"); - - String query = gson.toJson(queryList); - String url = queryExaremeUrl + "/" + "DESCRIPTIVE_STATS_v2"; - - try { - StringBuilder results = new StringBuilder(); - int code = HTTPUtil.sendPost(url, query, results); - - Logging.LogUserAction(userInfo.getUser().getUsername(), "(POST) /experiments/descriptive_stats_v2", "Successfully executed descriptive stats v2 with results : " + results.toString()); - return ResponseEntity.ok(gson.toJson(results.toString())); - } catch (IOException e) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(POST) /experiments/descriptive_stats_v2", "Descriptive stats v2 algorithm was not found"); - return new ResponseEntity<>("Not found", HttpStatus.NOT_FOUND); - } - } - - @ApiOperation(value = "Check if a formula is valid", response = String.class) - @RequestMapping(value = "/checkFormula", method = RequestMethod.POST) - public ResponseEntity checkFormulaValidity(String formula) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(POST) /experiments/checkFormula", "Executing checkFormula ..."); - - return ResponseEntity.ok(""); - } -} diff --git a/src/main/java/eu/hbp/mip/controllers/ModelsApi.java b/src/main/java/eu/hbp/mip/controllers/ModelsApi.java deleted file mode 100644 index 6f013ee33..000000000 --- a/src/main/java/eu/hbp/mip/controllers/ModelsApi.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Created by mirco on 04.12.15. - */ - -package eu.hbp.mip.controllers; - -import com.github.slugify.Slugify; -import eu.hbp.mip.model.Model; -import eu.hbp.mip.model.User; -import eu.hbp.mip.model.UserInfo; -import eu.hbp.mip.model.Variable; -import eu.hbp.mip.repositories.*; -import io.swagger.annotations.*; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import eu.hbp.mip.utils.Logging; - -import java.io.IOException; -import java.util.*; - -import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; - -@RestController -@RequestMapping(value = "/models", produces = {APPLICATION_JSON_VALUE}) -@Api(value = "/models", description = "the models API") -public class ModelsApi { - - - @Autowired - private UserInfo userInfo; - - @Autowired - private DatasetRepository datasetRepository; - - @Autowired - private ModelRepository modelRepository; - - @Autowired - private QueryRepository queryRepository; - - @Autowired - private ConfigRepository configRepository; - - @Autowired - private VariableRepository variableRepository; - - @ApiOperation(value = "get models", response = Model.class, responseContainer = "List") - @RequestMapping(method = RequestMethod.GET) - public ResponseEntity<List> getModels( - @ApiParam(value = "Only ask own models") @RequestParam(value = "own", required = false) Boolean own, - @ApiParam(value = "Only ask published models") @RequestParam(value = "valid", required = false) Boolean valid - ) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /models", "Loading models ..."); - - User user = userInfo.getUser(); - - Iterable<Model> models; - if (own != null && own) { - models = modelRepository.findByCreatedByOrderByCreatedAt(user); - } else { - models = modelRepository.findByValidOrCreatedByOrderByCreatedAt(true, user); - } - - if (valid != null && models != null) { - for (Iterator<Model> i = models.iterator(); i.hasNext(); ) { - Model m = i.next(); - if (valid != m.getValid()) { - i.remove(); - } - } - } - - List<Object> modelsList = new LinkedList<>(); - models = models != null ? models : new LinkedList<>(); - for (Model m : models) { - m.setDataset(datasetRepository.findOne(m.getDataset().getCode())); - modelsList.add(m); - } - - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /models", "Successfully loaded " + modelsList.size() + " models."); - - return ResponseEntity.ok(modelsList); - } - - - @ApiOperation(value = "Create a model", response = Model.class) - @ApiResponses(value = {@ApiResponse(code = 201, message = "Model created")}) - @RequestMapping(method = RequestMethod.POST) - public ResponseEntity<Model> addAModel( - @RequestBody @ApiParam(value = "Model to create", required = true) Model model - ) { - User user = userInfo.getUser(); - Logging.LogUserAction(user.getUsername(), "(POST) /models", "Creating a model"); - - model.setTitle(model.getConfig().getTitle().get("text")); - model.setCreatedBy(user); - model.setCreatedAt(new Date()); - if (model.getValid() == null) { - model.setValid(false); - } - - ensureTitleUniqueness(model); - ensureSlugUniqueness(model); - - Map<String, String> map = new HashMap<>(model.getConfig().getTitle()); - map.put("text", model.getTitle()); - model.getConfig().setTitle(map); - - saveVariables(model.getQuery().getVariables()); - saveVariables(model.getQuery().getCovariables()); - saveVariables(model.getQuery().getGrouping()); - saveVariables(model.getQuery().getTrainingDatasets()); - - configRepository.save(model.getConfig()); - queryRepository.save(model.getQuery()); - if (model.getDataset() != null) { - datasetRepository.save(model.getDataset()); - } - modelRepository.save(model); - - Logging.LogUserAction(user.getUsername(), "(POST) /models", "Created model with id : " + model.getSlug() + ", model.config and model.query"); - - return ResponseEntity.status(HttpStatus.CREATED).body(model); - } - - private void saveVariables(@RequestBody @ApiParam(value = "Model to create", required = true) List<Variable> variables) { - for (Variable var : variables) { - variableRepository.save(var); - } - } - - private void ensureSlugUniqueness(@RequestBody @ApiParam(value = "Model to create", required = true) Model model) { - String slug = createSlug(model.getTitle()); - boolean slugExists = true; - for (int i = 1; slugExists; i++) { - slugExists = modelRepository.exists(slug); - if (slugExists) { - if (i > 1) { - slug = slug.substring(0, slug.length() - 2); - } - slug += "-" + i; - } - model.setSlug(slug); - } - } - - private String createSlug(@RequestBody @ApiParam(value = "Model to create", required = true) String title) { - String slug; - try { - slug = new Slugify().slugify(title); - } catch (IOException e) { - slug = ""; // Should never happen - //LOGGER.trace("Cannot slugify title", e); - } - return slug; - } - - private void ensureTitleUniqueness(@RequestBody @ApiParam(value = "Model to create", required = true) Model model) { - boolean titleExists = true; - for (int i = 1; titleExists; i++) { - String title = model.getTitle(); - titleExists = modelRepository.countByTitle(title) > 0; - if (titleExists) { - if (i > 1) { - title = title.substring(0, title.length() - 4); - } - model.setTitle(title + " (" + i + ")"); - } - } - } - - @ApiOperation(value = "Get a model", response = Model.class) - @RequestMapping(value = "/{slug}", method = RequestMethod.GET) - public ResponseEntity<Model> getAModel( - @ApiParam(value = "slug", required = true) @PathVariable("slug") String slug - ) { - - User user = userInfo.getUser(); - - Logging.LogUserAction(user.getUsername(), "(GET) /models/{slug}", "Loading model with id : " + slug); - - Model model = modelRepository.findOne(slug); - if (model == null) { - //LOGGER.warn("Cannot find model : " + slug); - Logging.LogUserAction(user.getUsername(), "(GET) /models/{slug}", "Model was not found"); - return ResponseEntity.badRequest().body(null); - } - - if (!model.getValid() && !model.getCreatedBy().getUsername().equals(user.getUsername())) { - Logging.LogUserAction(user.getUsername(), "(GET) /models/{slug}", "You are not authorized to retrieve models. "); - return new ResponseEntity<>(HttpStatus.FORBIDDEN); - } - - List<String> yAxisVars = configRepository.findOne(model.getConfig().getId()).getyAxisVariables(); - Collection<String> yAxisVarsColl = new LinkedHashSet<>(yAxisVars); - model.getConfig().setyAxisVariables(new LinkedList<>(yAxisVarsColl)); - - Logging.LogUserAction(user.getUsername(), "(GET) /models/{slug}", "Loaded model with id : " + slug); - return ResponseEntity.ok(model); - } - - - @ApiOperation(value = "Update a model", response = Void.class) - @ApiResponses(value = {@ApiResponse(code = 204, message = "Model updated")}) - @RequestMapping(value = "/{slug}", method = RequestMethod.PUT) - public ResponseEntity<Void> updateAModel( - @ApiParam(value = "slug", required = true) @PathVariable("slug") String slug, - @RequestBody @ApiParam(value = "Model to update", required = true) Model model - ) { - User user = userInfo.getUser(); - Logging.LogUserAction(user.getUsername(), "(PUT) /models/{slug}", "Updating model with id : " + slug); - Model oldModel = modelRepository.findOne(slug); - - if (!user.getUsername().equals(oldModel.getCreatedBy().getUsername())) { - return new ResponseEntity<>(HttpStatus.FORBIDDEN); - } - - model.setTitle(model.getConfig().getTitle().get("text")); - - String oldTitle = oldModel.getTitle(); - String newTitle = model.getTitle(); - - // If title has been updated, ensure it is unique - if (!newTitle.equals(oldTitle)) { - boolean newTitleExists = true; - for (int i = 1; newTitleExists && !newTitle.equals(oldTitle); i++) { - newTitle = model.getTitle(); - newTitleExists = modelRepository.countByTitle(newTitle) > 0; - if (newTitleExists && !newTitle.equals(oldTitle)) { - if (i > 1) { - newTitle = newTitle.substring(0, newTitle.length() - 4); - } - model.setTitle(newTitle + " (" + i + ")"); - } - } - } - - Map<String, String> map = new HashMap<>(model.getConfig().getTitle()); - map.put("text", model.getTitle()); - model.getConfig().setTitle(map); - - saveVariables(model.getQuery().getVariables()); - saveVariables(model.getQuery().getCovariables()); - saveVariables(model.getQuery().getGrouping()); - saveVariables(model.getQuery().getTrainingDatasets()); - - configRepository.save(model.getConfig()); - queryRepository.save(model.getQuery()); - datasetRepository.save(model.getDataset()); - modelRepository.save(model); - - Logging.LogUserAction(user.getUsername(), "(PUT) /models/{slug}", "Updated model and saved/updated model.config and model.query"); - - return new ResponseEntity<>(HttpStatus.NO_CONTENT); - } - -} diff --git a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java index 779169814..66a97896b 100644 --- a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java +++ b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java @@ -6,7 +6,7 @@ package eu.hbp.mip.controllers; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; -import eu.hbp.mip.model.PathologyDTO; +import eu.hbp.mip.model.DTOs.PathologyDTO; import eu.hbp.mip.model.UserInfo; import eu.hbp.mip.utils.ClaimUtils; import eu.hbp.mip.utils.CustomResourceLoader; diff --git a/src/main/java/eu/hbp/mip/controllers/StatsApi.java b/src/main/java/eu/hbp/mip/controllers/StatsApi.java deleted file mode 100644 index 8bd109e53..000000000 --- a/src/main/java/eu/hbp/mip/controllers/StatsApi.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Created by mirco on 18.01.16. - */ - -package eu.hbp.mip.controllers; - -import eu.hbp.mip.model.GeneralStats; -import eu.hbp.mip.model.UserInfo; -import eu.hbp.mip.repositories.ArticleRepository; -import eu.hbp.mip.repositories.UserRepository; -import eu.hbp.mip.utils.Logging; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; - -import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; - -@RestController -@RequestMapping(value = "/stats", produces = {APPLICATION_JSON_VALUE}) -@Api(value = "/stats", description = "the stats API") -public class StatsApi { - @Autowired - private UserRepository userRepository; - - @Autowired - private UserInfo userInfo; - - @Autowired - private ArticleRepository articleRepository; - - - @ApiOperation(value = "Get general statistics", response = GeneralStats.class) - @RequestMapping(method = RequestMethod.GET) - public ResponseEntity<GeneralStats> getGeneralStatistics() { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /stats", "Loading general statistics"); - - GeneralStats stats = new GeneralStats(); - - stats.setUsers(userRepository.count()); - stats.setArticles(articleRepository.count()); - - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /stats", "Loaded " + userRepository.count() + " user statistics and " + articleRepository.count() + " artcle statistics."); - return ResponseEntity.ok(stats); - } - -} diff --git a/src/main/java/eu/hbp/mip/model/Article.java b/src/main/java/eu/hbp/mip/model/Article.java index 95c7cbbae..b79f2a33a 100644 --- a/src/main/java/eu/hbp/mip/model/Article.java +++ b/src/main/java/eu/hbp/mip/model/Article.java @@ -52,10 +52,6 @@ public class Article { @JoinColumn(name = "updatedby_username") private User updatedBy = null; - @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) - private List<Tag> tags = new LinkedList<>(); - - public Article() { /* * Empty constructor is needed by Hibernate @@ -154,13 +150,4 @@ public class Article { this.updatedBy = updatedBy; } - - public List<Tag> getTags() { - return tags; - } - - public void setTags(List<Tag> tags) { - this.tags = tags; - } - } diff --git a/src/main/java/eu/hbp/mip/model/Config.java b/src/main/java/eu/hbp/mip/model/Config.java deleted file mode 100644 index 2362194b2..000000000 --- a/src/main/java/eu/hbp/mip/model/Config.java +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Created by mirco on 25.02.16. - */ - -package eu.hbp.mip.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import io.swagger.annotations.ApiModel; - -import javax.persistence.*; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -@Entity -@Table(name = "`config`") -@ApiModel -@JsonIgnoreProperties(value = {"id"}) -public class Config { - - @Id - @GeneratedValue(strategy = GenerationType.SEQUENCE) - private Long id = null; - - private String type = null; - - private Integer height = null; - - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "config_yAxisVariables", joinColumns = @JoinColumn(name = "config_id")) - private List<String> yAxisVariables = new LinkedList<>(); - - private String xAxisVariable = null; - - private Boolean hasXAxis = null; - - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "config_title", joinColumns = @JoinColumn(name = "config_id")) - private Map<String, String> title = new HashMap<>(); - - - public Config() { - /* - * Empty constructor is needed by Hibernate - */ - } - - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - - public Integer getHeight() { - return height; - } - - public void setHeight(Integer height) { - this.height = height; - } - - - public List<String> getyAxisVariables() { - return yAxisVariables; - } - - public void setyAxisVariables(List<String> yAxisVariables) { - this.yAxisVariables = yAxisVariables; - } - - - public String getxAxisVariable() { - return xAxisVariable; - } - - public void setxAxisVariable(String xAxisVariable) { - this.xAxisVariable = xAxisVariable; - } - - - public Boolean getHasXAxis() { - return hasXAxis; - } - - public void setHasXAxis(Boolean hasXAxis) { - this.hasXAxis = hasXAxis; - } - - - public Map<String, String> getTitle() { - return title; - } - - public void setTitle(Map<String, String> title) { - this.title = title; - } - -} diff --git a/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java b/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java new file mode 100644 index 000000000..682f121ce --- /dev/null +++ b/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java @@ -0,0 +1,203 @@ +package eu.hbp.mip.model.DAOs; + +import com.google.gson.Gson; +import com.google.gson.annotations.Expose; +import eu.hbp.mip.model.DTOs.AlgorithmDTO; +import eu.hbp.mip.model.DTOs.ExperimentDTO; +import eu.hbp.mip.model.User; +import eu.hbp.mip.utils.JsonConverters; + +import javax.persistence.*; +import java.util.Date; +import java.util.UUID; + +/** + * Created by habfast on 21/04/16. + */ +@Entity +@Table(name = "`experiment`") +public class ExperimentDAO { + + private static final Gson gson = new Gson(); + + @Id + @Column(columnDefinition = "uuid", updatable = false) + @org.hibernate.annotations.Type(type = "pg-uuid") + @Expose + private UUID uuid; + + @Column(columnDefinition = "TEXT") + @Expose + private String name; + + @Expose + @ManyToOne + @JoinColumn(name = "created_by_username") + private User createdBy; + + @Column(name="workflow_history_id", columnDefinition = "TEXT") + @Expose + private String workflowHistoryId; + + @Column(columnDefinition = "TEXT") + @Expose + private Status status; + + @Column(columnDefinition = "TEXT") + @Expose + private String result; + + @Expose + private Date finished; + + @Expose + private String algorithm; + + @Expose + private Date created = new Date(); + + @Expose + private boolean shared = false; + + // whether or not the experiment's result have been viewed by its owner + @Expose + private boolean viewed = false; + + public enum Status { + error, + pending, + success + } + + public enum MimeTypes { + ERROR("text/plain+error"), + WARNING("text/plain+warning"), + USER_WARNING("text/plain+user_error"), + HIGHCHARTS("application/vnd.highcharts+json"), + JSON("application/json"), + JSONBTREE("application/binary-tree+json"), + PFA("application/pfa+json"), + JSONDATA("application/vnd.dataresource+json"), + HTML("text/html"), + TEXT("text/plain"); + + private String types; + + //Constructor to initialize the instance variable + MimeTypes(String types) { + this.types = types; + } + + public String getTypes() { + return this.types; + } + } + + public ExperimentDAO() { + /* + * Empty constructor is needed by Hibernate + */ + } + + public ExperimentDTO convertToDTO() + { + ExperimentDTO experimentDTO = new ExperimentDTO(); + experimentDTO.setAlgorithm(JsonConverters.convertJsonStringToObject(this.algorithm, AlgorithmDTO.class)); + experimentDTO.setCreated(this.created); + experimentDTO.setCreatedBy(this.createdBy.getUsername()); + experimentDTO.setName(this.name); + experimentDTO.setResult(JsonConverters.convertJsonStringToObject(this.result, ExperimentDTO.ResultDTO.class)); + experimentDTO.setShared(this.shared); + experimentDTO.setUuid(this.uuid.toString()); + experimentDTO.setViewed(this.viewed); + return experimentDTO; + } + + public String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public String getWorkflowHistoryId() { + return workflowHistoryId; + } + + public void setWorkflowHistoryId(String workflowHistoryId) { + this.workflowHistoryId = workflowHistoryId; + } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + + public String getResult() { + return result; + } + + public void setResult(String result) { + this.result = result; + } + + public Date getFinished() { + return finished; + } + + public void setFinished(Date finished) { + this.finished = finished; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public UUID getUuid() { + return uuid; + } + + public void setUuid(UUID uuid) { + this.uuid = uuid; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public User getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(User createdBy) { + this.createdBy = createdBy; + } + + public boolean isViewed() { + return viewed; + } + + public void setViewed(boolean viewed) { + this.viewed = viewed; + } + + public boolean isShared() { + return shared; + } + + public void setShared(boolean shared) { + this.shared = shared; + } +} diff --git a/src/main/java/eu/hbp/mip/model/AlgorithmDTO.java b/src/main/java/eu/hbp/mip/model/DTOs/AlgorithmDTO.java similarity index 95% rename from src/main/java/eu/hbp/mip/model/AlgorithmDTO.java rename to src/main/java/eu/hbp/mip/model/DTOs/AlgorithmDTO.java index ed0ef2a15..0a49c5ad6 100644 --- a/src/main/java/eu/hbp/mip/model/AlgorithmDTO.java +++ b/src/main/java/eu/hbp/mip/model/DTOs/AlgorithmDTO.java @@ -1,4 +1,4 @@ -package eu.hbp.mip.model; +package eu.hbp.mip.model.DTOs; import com.google.gson.annotations.SerializedName; diff --git a/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java b/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java new file mode 100644 index 000000000..b4214dd0a --- /dev/null +++ b/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java @@ -0,0 +1,126 @@ +package eu.hbp.mip.model.DTOs; + +import eu.hbp.mip.model.DAOs.ExperimentDAO; + +import java.util.Date; +import java.util.List; + +public class ExperimentDTO { + + private String uuid; + private String name; + private String createdBy; + private Date created; + private Boolean shared; + private Boolean viewed; + private ExperimentDTO.ResultDTO result; + private ExperimentDAO.Status status; + + private AlgorithmDTO algorithm; + + public AlgorithmDTO getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(AlgorithmDTO algorithm) { + this.algorithm = algorithm; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Boolean getShared() { + return shared; + } + + public void setShared(Boolean shared) { + this.shared = shared; + } + + public Boolean getViewed() { + return viewed; + } + + public void setViewed(Boolean viewed) { + this.viewed = viewed; + } + + public ExperimentDTO.ResultDTO getResult() { + return result; + } + + public void setResult(ExperimentDTO.ResultDTO result) { + this.result = result; + } + + public ExperimentDAO.Status getStatus() { + return status; + } + + public void setStatus(ExperimentDAO.Status status) { + this.status = status; + } + + public static class OutputDTO { + + private String data; + private ExperimentDAO.MimeTypes mimeTypes; + + public String getData() { + return this.data; + } + + public void setData(String data) { + this.data = data; + } + + public ExperimentDAO.MimeTypes getMimeTypes() { + return mimeTypes; + } + + public void setMimeTypes(ExperimentDAO.MimeTypes mimeTypes) { + this.mimeTypes = mimeTypes; + } + } + + public static class ResultDTO { + private List<OutputDTO> result; + + public List<OutputDTO> getResult() { + return this.result; + } + + public void setResult(List<OutputDTO> result) { + this.result = result; + } + } +} diff --git a/src/main/java/eu/hbp/mip/model/PathologyDTO.java b/src/main/java/eu/hbp/mip/model/DTOs/PathologyDTO.java similarity index 93% rename from src/main/java/eu/hbp/mip/model/PathologyDTO.java rename to src/main/java/eu/hbp/mip/model/DTOs/PathologyDTO.java index c58238b62..6a4580e81 100644 --- a/src/main/java/eu/hbp/mip/model/PathologyDTO.java +++ b/src/main/java/eu/hbp/mip/model/DTOs/PathologyDTO.java @@ -1,4 +1,4 @@ -package eu.hbp.mip.model; +package eu.hbp.mip.model.DTOs; import com.google.gson.annotations.SerializedName; diff --git a/src/main/java/eu/hbp/mip/model/Dataset.java b/src/main/java/eu/hbp/mip/model/Dataset.java deleted file mode 100644 index d920571d9..000000000 --- a/src/main/java/eu/hbp/mip/model/Dataset.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Created by mirco on 04.12.15. - */ - -package eu.hbp.mip.model; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.annotations.ApiModel; - -import javax.persistence.*; -import java.util.Date; -import java.util.LinkedList; -import java.util.List; - -// TODO: deprecate - -@Entity -@Table(name = "`dataset`") -@ApiModel -@JsonInclude(JsonInclude.Include.NON_NULL) -public class Dataset { - - @Id - private String code = null; - - private Date date = null; - - // Aka covariables - @ElementCollection - @CollectionTable(name = "dataset_header", joinColumns = @JoinColumn(name = "dataset_code")) - private List<String> header = new LinkedList<>(); - - @ElementCollection - @CollectionTable(name = "dataset_grouping", joinColumns = @JoinColumn(name = "dataset_code")) - private List<String> grouping = new LinkedList<>(); - - @ElementCollection - @CollectionTable(name = "dataset_variable", joinColumns = @JoinColumn(name = "dataset_code")) - private List<String> variable = new LinkedList<>(); - - // TODO: - @Transient - @JsonProperty(access = JsonProperty.Access.READ_ONLY) - private Object data = null; - - - public Dataset() { - /* - * Empty constructor is needed by Hibernate - */ - } - - - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - - public Date getDate() { - return date; - } - - public void setDate(Date date) { - this.date = date; - } - - - public List<String> getHeader() { - return header; - } - - public void setHeader(List<String> header) { - this.header = header; - } - - - public List<String> getGrouping() { - return grouping; - } - - public void setGrouping(List<String> grouping) { - this.grouping = grouping; - } - - - public List<String> getVariable() { - return variable; - } - - public void setVariable(List<String> variable) { - this.variable = variable; - } - - public Object getData() { - return data; - } - - public void setData(Object data) { - this.data = data; - } -} diff --git a/src/main/java/eu/hbp/mip/model/DatasetDescription.java b/src/main/java/eu/hbp/mip/model/DatasetDescription.java deleted file mode 100644 index c77c50aea..000000000 --- a/src/main/java/eu/hbp/mip/model/DatasetDescription.java +++ /dev/null @@ -1,46 +0,0 @@ -package eu.hbp.mip.model; - -import com.fasterxml.jackson.annotation.JsonInclude; -import io.swagger.annotations.ApiModel; - -@ApiModel -@JsonInclude(JsonInclude.Include.NON_NULL) -public class DatasetDescription { - - private String code; - private String label; - private String description; - private String anonymisationLevel; - - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getAnonymisationLevel() { - return anonymisationLevel; - } - - public void setAnonymisationLevel(String anonymisationLevel) { - this.anonymisationLevel = anonymisationLevel; - } -} diff --git a/src/main/java/eu/hbp/mip/model/Experiment.java b/src/main/java/eu/hbp/mip/model/Experiment.java deleted file mode 100644 index c40d91d34..000000000 --- a/src/main/java/eu/hbp/mip/model/Experiment.java +++ /dev/null @@ -1,232 +0,0 @@ -package eu.hbp.mip.model; - -import com.google.gson.*; -import com.google.gson.annotations.Expose; -import org.hibernate.annotations.Cascade; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.persistence.*; -import java.util.*; - -/** - * Created by habfast on 21/04/16. - */ -@Entity -@Table(name = "`experiment`") -public class Experiment { - - private static final Gson gson = new Gson(); - - @Id - @Column(columnDefinition = "uuid") - @org.hibernate.annotations.Type(type = "pg-uuid") - @Expose - private UUID uuid; - - @Column(columnDefinition = "TEXT") - @Expose - private String name; - - @Expose - @ManyToOne - @JoinColumn(name = "createdby_username") - private User createdBy; - - @ManyToOne - @Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE) - @Expose - private Model model; - - @Column(columnDefinition = "TEXT") - @Expose - private String algorithms; - - @Column(columnDefinition = "TEXT") - private String validations; - - @Column(columnDefinition = "TEXT") - @Expose - private String workflowHistoryId; - - @Column(columnDefinition = "TEXT") - @Expose - private String workflowStatus; - - @Column(columnDefinition = "TEXT") - @Expose - private String result; - - @Expose - private Date created = new Date(); - - @Expose - private Date finished; - - @Expose - private boolean hasError = false; - - @Expose - private boolean hasServerError = false; - - @Expose - private boolean shared = false; - - // whether or not the experiment's result have been resultsViewed by its owner - @Expose - private boolean resultsViewed = false; - - public Experiment() { - /* - * Empty constructor is needed by Hibernate - */ - } - - - public JsonObject jsonify() { - JsonObject exp = gson.toJsonTree(this).getAsJsonObject(); - JsonParser parser = new JsonParser(); - - if (this.algorithms != null) { - exp.remove("algorithms"); - JsonArray jsonAlgorithms = parser.parse(this.algorithms).getAsJsonArray(); - exp.add("algorithms", jsonAlgorithms); - } - - if (this.validations != null) { - exp.remove("validations"); - JsonArray jsonValidations = parser.parse(this.validations).getAsJsonArray(); - exp.add("validations", jsonValidations); - } - - if (this.result != null && !this.hasServerError) { - exp.remove("result"); - - JsonElement jsonResult = parser.parse(this.result); - exp.add("result", jsonResult); - - } - - return exp; - } - - public String getValidations() { - return validations; - } - - public void setValidations(String validations) { - this.validations = validations; - } - - public String getAlgorithms() { - return algorithms; - } - - public void setAlgorithms(String algorithms) { - this.algorithms = algorithms; - } - - public Model getModel() { - return model; - } - - public void setModel(Model model) { - this.model = model; - } - - public boolean isHasError() { - return hasError; - } - - public void setHasError(boolean hasError) { - this.hasError = hasError; - } - - public String getWorkflowHistoryId() { - return workflowHistoryId; - } - - public void setWorkflowHistoryId(String workflowHistoryId) { - this.workflowHistoryId = workflowHistoryId; - } - - public String getWorkflowStatus() { - return workflowStatus; - } - - public void setWorkflowStatus(String workflowStatus) { - this.workflowStatus = workflowStatus; - } - - public String getResult() { - return result; - } - - public void setResult(String result) { - this.result = result; - } - - public Date getFinished() { - return finished; - } - - public void setFinished(Date finished) { - this.finished = finished; - } - - public Date getCreated() { - return created; - } - - public void setCreated(Date created) { - this.created = created; - } - - public UUID getUuid() { - return uuid; - } - - public void setUuid(UUID uuid) { - this.uuid = uuid; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public User getCreatedBy() { - return createdBy; - } - - public void setCreatedBy(User createdBy) { - this.createdBy = createdBy; - } - - public boolean isResultsViewed() { - return resultsViewed; - } - - public void setResultsViewed(boolean resultsViewed) { - this.resultsViewed = resultsViewed; - } - - public boolean isHasServerError() { - return hasServerError; - } - - public void setHasServerError(boolean hasServerError) { - this.hasServerError = hasServerError; - } - - public boolean isShared() { - return shared; - } - - public void setShared(boolean shared) { - this.shared = shared; - } -} diff --git a/src/main/java/eu/hbp/mip/model/ExperimentExecutionDTO.java b/src/main/java/eu/hbp/mip/model/ExperimentExecutionDTO.java deleted file mode 100644 index a68cd9121..000000000 --- a/src/main/java/eu/hbp/mip/model/ExperimentExecutionDTO.java +++ /dev/null @@ -1,106 +0,0 @@ -package eu.hbp.mip.model; - -import java.util.List; - -public class ExperimentExecutionDTO { - - private String name; - private String model; - private List<AlgorithmExecutionDTO> algorithms; - - public String getModel() { - return model; - } - - public void setModel(String model) { - this.model = model; - } - - public List<AlgorithmExecutionDTO> getAlgorithms() { - return algorithms; - } - - public void setAlgorithms(List<AlgorithmExecutionDTO> algorithms) { - this.algorithms = algorithms; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public static class AlgorithmExecutionDTO { - - private String name; - private String label; - private String type; - - private List<AlgorithmExecutionParamDTO> parameters; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public List<AlgorithmExecutionDTO.AlgorithmExecutionParamDTO> getParameters() { - return parameters; - } - - public void setParameters(List<AlgorithmExecutionDTO.AlgorithmExecutionParamDTO> parameters) { - this.parameters = parameters; - } - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - - public static class AlgorithmExecutionParamDTO { - - private String name; - private String label; - private String value; - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - } - } -} diff --git a/src/main/java/eu/hbp/mip/model/Group.java b/src/main/java/eu/hbp/mip/model/Group.java deleted file mode 100644 index abb195a93..000000000 --- a/src/main/java/eu/hbp/mip/model/Group.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Created by mirco on 04.12.15. - */ - -package eu.hbp.mip.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import io.swagger.annotations.ApiModel; - -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; - -@Entity -@Table(name = "`group`") -@ApiModel -@JsonIgnoreProperties(value = {"parent"}) -@JsonInclude(JsonInclude.Include.NON_NULL) -public class Group { - - @Id - private String code = null; - - - public Group() { - /* - * Empty constructor is needed by Hibernate - */ - } - - - public String getCode() { - return code; - } - -} diff --git a/src/main/java/eu/hbp/mip/model/Mining.java b/src/main/java/eu/hbp/mip/model/Mining.java deleted file mode 100644 index 46cfd6c3e..000000000 --- a/src/main/java/eu/hbp/mip/model/Mining.java +++ /dev/null @@ -1,64 +0,0 @@ -package eu.hbp.mip.model; - -import com.google.gson.*; - -import java.util.Date; - -public class Mining { - - private static final Gson gson = new Gson(); - - private final String jobId; - private final String node; - private final String function; - private final String shape; - private final Date timestamp; - private final String data; - - public Mining(String jobId, String node, String function, String shape, Date timestamp, String data) { - this.jobId = jobId; - this.node = node; - this.function = function; - this.shape = shape; - this.timestamp = timestamp; - this.data = data; - } - - public String getJobId() { - return jobId; - } - - public String getNode() { - return node; - } - - public String getFunction() { - return function; - } - - public String getShape() { - return shape; - } - - public Date getTimestamp() { - return timestamp; - } - - public String getData() { - return data; - } - - public JsonObject jsonify() { - JsonObject exp = gson.toJsonTree(this).getAsJsonObject(); - JsonParser parser = new JsonParser(); - - if (this.data != null) { - exp.remove("data"); - JsonElement jsonResult = parser.parse(this.data); - exp.add("data", jsonResult); - } - - return exp; - } - -} diff --git a/src/main/java/eu/hbp/mip/model/MiningQuery.java b/src/main/java/eu/hbp/mip/model/MiningQuery.java deleted file mode 100644 index 171c55032..000000000 --- a/src/main/java/eu/hbp/mip/model/MiningQuery.java +++ /dev/null @@ -1,96 +0,0 @@ -package eu.hbp.mip.model; - -import com.google.gson.Gson; - -import java.util.LinkedList; -import java.util.List; - -/** - * Created by mirco on 06.01.17. - */ -public class MiningQuery { - - private List<Variable> variables; - private List<Variable> covariables; - private List<Variable> grouping; - private List<Variable> datasets; - private String filters; - private ExperimentExecutionDTO.AlgorithmExecutionDTO algorithm; - - public MiningQuery() { - this.variables = new LinkedList<>(); - this.covariables = new LinkedList<>(); - this.grouping = new LinkedList<>(); - this.datasets = new LinkedList<>(); - this.filters = ""; - } - - public List<Variable> getVariables() { - return variables; - } - - public void setVariables(List<Variable> variables) { - this.variables = variables; - } - - public void addVariable(Variable variable) { - this.variables.add(variable); - } - - public List<Variable> getCovariables() { - return covariables; - } - - public void setCovariables(List<Variable> covariables) { - this.covariables = covariables; - } - - public void addCovariable(Variable variable) { - this.covariables.add(variable); - } - - public List<Variable> getGrouping() { - return grouping; - } - - public void setGrouping(List<Variable> grouping) { - this.grouping = grouping; - } - - public List<Variable> getDatasets() { - return datasets; - } - - public void setDataset(List<Variable> datasets) { - this.datasets = datasets; - } - - public void addDataset(Variable variable) { - this.datasets.add(variable); - } - - public void addGrouping(Variable variable) { - this.grouping.add(variable); - } - - public String getFilters() { - return filters; - } - - public void setFilters(String filters) { - this.filters = filters; - } - - public ExperimentExecutionDTO.AlgorithmExecutionDTO getAlgorithm() { - return algorithm; - } - - public void setAlgorithm(ExperimentExecutionDTO.AlgorithmExecutionDTO algorithm) { - this.algorithm = algorithm; - } - - @Override - public String toString() { - return new Gson().toJson(this); - } -} diff --git a/src/main/java/eu/hbp/mip/model/Model.java b/src/main/java/eu/hbp/mip/model/Model.java deleted file mode 100644 index b15908037..000000000 --- a/src/main/java/eu/hbp/mip/model/Model.java +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Created by mirco on 04.12.15. - */ - -package eu.hbp.mip.model; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.google.gson.annotations.Expose; -import io.swagger.annotations.ApiModel; -import org.hibernate.annotations.Cascade; -import org.hibernate.annotations.CascadeType; - -import javax.persistence.*; -import java.util.Date; - -@Entity -@Table(name = "`model`") -@ApiModel -@JsonInclude(JsonInclude.Include.NON_NULL) -public class Model { - - @Id - @Expose - private String slug = null; - - @Expose - private String title = null; - - private String description = null; - - private Boolean valid = null; - - private Date createdAt = null; - - private Date updatedAt = null; - - @ManyToOne - @Cascade(CascadeType.SAVE_UPDATE) - private eu.hbp.mip.model.Query query = null; - - @ManyToOne - @Cascade(CascadeType.SAVE_UPDATE) - private Dataset dataset = null; - - @ManyToOne - @Cascade(CascadeType.SAVE_UPDATE) - private Config config = null; - - @ManyToOne - @JoinColumn(name = "createdby_username") - private User createdBy = null; - - @ManyToOne - @JoinColumn(name = "updatedby_username") - private User updatedBy = null; - - - public Model() { - /* - * Empty constructor is needed by Hibernate - */ - } - - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - - public String getSlug() { - return slug; - } - - public void setSlug(String slug) { - this.slug = slug; - } - - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - - public eu.hbp.mip.model.Query getQuery() { - return query; - } - - public void setQuery(eu.hbp.mip.model.Query query) { - this.query = query; - } - - - public Dataset getDataset() { - return dataset; - } - - public void setDataset(Dataset dataset) { - this.dataset = dataset; - } - - - public Boolean getValid() { - return valid; - } - - public void setValid(Boolean valid) { - this.valid = valid; - } - - - public Config getConfig() { - return config; - } - - public void setConfig(Config config) { - this.config = config; - } - - - public Date getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(Date createdAt) { - this.createdAt = createdAt; - } - - - public Date getUpdatedAt() { - return updatedAt; - } - - public void setUpdatedAt(Date updatedAt) { - this.updatedAt = updatedAt; - } - - - public User getCreatedBy() { - return createdBy; - } - - public void setCreatedBy(User createdBy) { - this.createdBy = createdBy; - } - - - public User getUpdatedBy() { - return updatedBy; - } - - public void setUpdatedBy(User updatedBy) { - this.updatedBy = updatedBy; - } - -} diff --git a/src/main/java/eu/hbp/mip/model/Query.java b/src/main/java/eu/hbp/mip/model/Query.java deleted file mode 100644 index 8e4d78dcb..000000000 --- a/src/main/java/eu/hbp/mip/model/Query.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Created by mirco on 04.12.15. - */ - -package eu.hbp.mip.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.annotations.ApiModel; - -import javax.persistence.*; -import java.util.LinkedList; -import java.util.List; - -@Entity -@ApiModel -@Table(name = "`query`") -@JsonIgnoreProperties(value = {"id"}) -@JsonInclude(JsonInclude.Include.NON_NULL) -public class Query { - - @Id - @GeneratedValue(strategy = GenerationType.SEQUENCE) - private Long id = null; - - @ManyToMany - @JoinTable(name = "query_variable", joinColumns = { - @JoinColumn(name = "id", nullable = false, updatable = false)}, - inverseJoinColumns = {@JoinColumn(name = "code", - nullable = false, updatable = false)}) - private List<Variable> variables = new LinkedList<>(); - - @ManyToMany - @JoinTable(name = "query_covariable", joinColumns = { - @JoinColumn(name = "id", nullable = false, updatable = false)}, - inverseJoinColumns = {@JoinColumn(name = "code", - nullable = false, updatable = false)}) - private List<Variable> covariables = new LinkedList<>(); - - @ManyToMany - @JoinTable(name = "query_grouping", joinColumns = { - @JoinColumn(name = "id", nullable = false, updatable = false)}, - inverseJoinColumns = {@JoinColumn(name = "code", - nullable = false, updatable = false)}) - private List<Variable> grouping = new LinkedList<>(); - - @ManyToMany - @JoinTable(name = "query_training_datasets", joinColumns = { - @JoinColumn(name = "id", nullable = false, updatable = false)}, - inverseJoinColumns = {@JoinColumn(name = "code", - nullable = false, updatable = false)}) - private List<Variable> trainingDatasets = new LinkedList<>(); - - @ManyToMany - @JoinTable(name = "query_testing_datasets", joinColumns = { - @JoinColumn(name = "id", nullable = false, updatable = false)}, - inverseJoinColumns = {@JoinColumn(name = "code", - nullable = false, updatable = false)}) - private List<Variable> testingDatasets = new LinkedList<>(); - - @ManyToMany - @JoinTable(name = "query_validation_datasets", joinColumns = { - @JoinColumn(name = "id", nullable = false, updatable = false)}, - inverseJoinColumns = {@JoinColumn(name = "code", - nullable = false, updatable = false)}) - private List<Variable> validationDatasets = new LinkedList<>(); - - @Column(columnDefinition = "text") - private String filters = ""; - - @Column(columnDefinition = "text") - private String pathology = ""; - - - public Query() { - /* - * Empty constructor is needed by Hibernate - */ - } - - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - - @JsonProperty("variables") - public List<Variable> getVariables() { - return variables; - } - - public void setVariables(List<Variable> variables) { - this.variables = variables; - } - - - @JsonProperty("coVariables") - public List<Variable> getCovariables() { - return covariables; - } - - public void setCovariables(List<Variable> covariables) { - this.covariables = covariables; - } - - - @JsonProperty("groupings") - public List<Variable> getGrouping() { - return grouping; - } - - public void setGrouping(List<Variable> grouping) { - this.grouping = grouping; - } - - @JsonProperty("trainingDatasets") - public List<Variable> getTrainingDatasets() { - return trainingDatasets; - } - - public void setTrainingDatasets(List<Variable> trainingDatasets) { - this.trainingDatasets = trainingDatasets; - } - - @JsonProperty("testingDatasets") - public List<Variable> getTestingDatasets() { - return testingDatasets; - } - - public void setTestingDatasets(List<Variable> testingDatasets) { - this.testingDatasets = testingDatasets; - } - - @JsonProperty("validationDatasets") - public List<Variable> getValidationDatasets() { - return validationDatasets; - } - - public void setValidationDatasets(List<Variable> validationDatasets) { - this.validationDatasets = validationDatasets; - } - - public String getFilters() { - return filters; - } - - public void setFilters(String filters) { - this.filters = filters; - } - - public String getPathology() { - return pathology; - } - - public void setPathology(String pathology) { - this.pathology = pathology; - } - -} diff --git a/src/main/java/eu/hbp/mip/model/Tag.java b/src/main/java/eu/hbp/mip/model/Tag.java deleted file mode 100644 index 693c98656..000000000 --- a/src/main/java/eu/hbp/mip/model/Tag.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Created by mirco on 04.12.15. - */ - -package eu.hbp.mip.model; - -import com.fasterxml.jackson.annotation.JsonInclude; -import io.swagger.annotations.ApiModel; - -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; - -@Entity -@Table(name = "`tag`") -@ApiModel -@JsonInclude(JsonInclude.Include.NON_NULL) -public class Tag { - - @Id - private String name = null; - - - public Tag() { - /* - * Empty constructor is needed by Hibernate - */ - } - - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - -} diff --git a/src/main/java/eu/hbp/mip/model/User.java b/src/main/java/eu/hbp/mip/model/User.java index 64fee4b67..f1ee34059 100644 --- a/src/main/java/eu/hbp/mip/model/User.java +++ b/src/main/java/eu/hbp/mip/model/User.java @@ -9,11 +9,11 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.google.gson.annotations.Expose; import io.swagger.annotations.ApiModel; -import javax.persistence.*; -import java.util.*; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; @Entity @Table(name = "`user`") @@ -29,58 +29,9 @@ public class User { @Expose private String fullname = null; - @Expose - private String firstname = null; - - @Expose - private String lastname = null; - - @Expose - private String picture = null; - - @Expose - private String web = null; - - @Expose - private String phone = null; - - @Expose - private String birthday = null; - - @Expose - private String gender = null; - - @Expose - private String city = null; - - @Expose - private String country = null; - - @Expose - private String password = null; - @Expose private String email = null; - @Expose - private String apikey = null; - - @Expose - private String team = null; - - @Expose - private Boolean isActive = null; - - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "user_languages", joinColumns = @JoinColumn(name = "user_username")) - @Expose - private List<String> languages = new LinkedList<>(); - - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_username")) - @Expose - private List<String> roles = new LinkedList<>(); - private Boolean agreeNDA = null; @@ -113,49 +64,12 @@ public class User { this.fullname = m.group(1); } - p = Pattern.compile("given_name=([\\w ]+)"); - m = p.matcher(userInfo); - if (m.find()) { - this.firstname = m.group(1); - } - - p = Pattern.compile("family_name=([\\w ]+)"); - m = p.matcher(userInfo); - if (m.find()) { - this.lastname = m.group(1); - } - p = Pattern.compile("email=([\\w.]+@[\\w.]+)"); m = p.matcher(userInfo); if (m.find()) { this.email = m.group(1); } - p = Pattern.compile("title=([\\w ]+)"); - m = p.matcher(userInfo); - if (m.find()) { - if ("Mr".equals(m.group(1))) { - this.gender = "Male"; - } else { - this.gender = "Female"; - } - } - - p = Pattern.compile("contractor=([\\w ]+)"); - m = p.matcher(userInfo); - if (m.find()) { - this.team = m.group(1); - } - - p = Pattern.compile("picture=([-a-zA-Z0-9+&@#/%=~_|.: ]+)"); - m = p.matcher(userInfo); - if (m.find()) { - this.picture = m.group(1); - } - - if (this.picture == null || this.picture.isEmpty()) { - this.picture = "images/users/default_user.png"; - } } @@ -178,96 +92,6 @@ public class User { } - public String getFirstname() { - return firstname; - } - - public void setFirstname(String firstname) { - this.firstname = firstname; - } - - - public String getLastname() { - return lastname; - } - - public void setLastname(String lastname) { - this.lastname = lastname; - } - - - public String getPicture() { - return picture; - } - - public void setPicture(String picture) { - this.picture = picture; - } - - - public String getWeb() { - return web; - } - - public void setWeb(String web) { - this.web = web; - } - - - public String getPhone() { - return phone; - } - - public void setPhone(String phone) { - this.phone = phone; - } - - - public String getBirthday() { - return birthday; - } - - public void setBirthday(String birthday) { - this.birthday = birthday; - } - - - public String getGender() { - return gender; - } - - public void setGender(String gender) { - this.gender = gender; - } - - - public String getCity() { - return city; - } - - public void setCity(String city) { - this.city = city; - } - - - public String getCountry() { - return country; - } - - public void setCountry(String country) { - this.country = country; - } - - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getEmail() { return email; } @@ -276,52 +100,6 @@ public class User { this.email = email; } - - public String getApikey() { - return apikey; - } - - public void setApikey(String apikey) { - this.apikey = apikey; - } - - - public String getTeam() { - return team; - } - - public void setTeam(String team) { - this.team = team; - } - - - public Boolean getIsActive() { - return isActive; - } - - public void setIsActive(Boolean isActive) { - this.isActive = isActive; - } - - - public List<String> getLanguages() { - return languages; - } - - public void setLanguages(List<String> languages) { - this.languages = languages; - } - - - public List<String> getRoles() { - return roles; - } - - public void setRoles(List<String> roles) { - this.roles = roles; - } - - public Boolean getAgreeNDA() { return agreeNDA; } diff --git a/src/main/java/eu/hbp/mip/model/UserInfo.java b/src/main/java/eu/hbp/mip/model/UserInfo.java index 71eee3573..7bd9a97f3 100644 --- a/src/main/java/eu/hbp/mip/model/UserInfo.java +++ b/src/main/java/eu/hbp/mip/model/UserInfo.java @@ -53,7 +53,6 @@ public class UserInfo { user.setUsername("anonymous"); user.setFullname("anonymous"); user.setEmail("anonymous@anonymous.com"); - user.setPicture("images/users/default_user.png"); } else { user = new User(getUserInfos()); } diff --git a/src/main/java/eu/hbp/mip/model/Value.java b/src/main/java/eu/hbp/mip/model/Value.java deleted file mode 100644 index 784452e59..000000000 --- a/src/main/java/eu/hbp/mip/model/Value.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Created by mirco on 04.12.15. - */ - -package eu.hbp.mip.model; - -import com.fasterxml.jackson.annotation.JsonInclude; -import io.swagger.annotations.ApiModel; - -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; - -@Entity -@Table(name = "`value`") -@ApiModel -@JsonInclude(JsonInclude.Include.NON_NULL) -public class Value { - - @Id - private String code = null; - - private String label = null; - - - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - -} diff --git a/src/main/java/eu/hbp/mip/model/Variable.java b/src/main/java/eu/hbp/mip/model/Variable.java deleted file mode 100644 index 596be8d77..000000000 --- a/src/main/java/eu/hbp/mip/model/Variable.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Created by mirco on 04.12.15. - */ - -package eu.hbp.mip.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.google.gson.annotations.Expose; -import io.swagger.annotations.ApiModel; - -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; -import java.util.List; - -@Entity -@Table(name = "`variable`") -@ApiModel -@JsonIgnoreProperties(value = {"queries"}) -@JsonInclude(JsonInclude.Include.NON_NULL) -public class Variable { - - @Id - @Expose - private String code = null; - - /** - * Empty constructor is needed by Hibernate - */ - public Variable() { - } - - public Variable(String code) { - this.code = code; - } - - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - public static String stringFromVariables(List<Variable> variables, String operator) { - StringBuilder sb = new StringBuilder(); - int i = 0; - for (Variable s : variables) { - i++; - sb.append(s.getCode()); - if (i < variables.size()) { - sb.append(operator); - } - } - return sb.toString(); - } - -} diff --git a/src/main/java/eu/hbp/mip/model/galaxy/WorkflowDTO.java b/src/main/java/eu/hbp/mip/model/galaxy/WorkflowDTO.java index fa0cb4b33..4b01b3819 100644 --- a/src/main/java/eu/hbp/mip/model/galaxy/WorkflowDTO.java +++ b/src/main/java/eu/hbp/mip/model/galaxy/WorkflowDTO.java @@ -2,7 +2,7 @@ package eu.hbp.mip.model.galaxy; import com.google.gson.Gson; import com.google.gson.annotations.SerializedName; -import eu.hbp.mip.model.AlgorithmDTO; +import eu.hbp.mip.model.DTOs.AlgorithmDTO; import java.util.*; diff --git a/src/main/java/eu/hbp/mip/repositories/ConfigRepository.java b/src/main/java/eu/hbp/mip/repositories/ConfigRepository.java deleted file mode 100644 index deb286150..000000000 --- a/src/main/java/eu/hbp/mip/repositories/ConfigRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package eu.hbp.mip.repositories; - -import eu.hbp.mip.model.Config; -import org.springframework.data.repository.CrudRepository; - -/** - * Created by mirco on 11.07.16. - */ - -public interface ConfigRepository extends CrudRepository<Config, Long> { -} diff --git a/src/main/java/eu/hbp/mip/repositories/DatasetRepository.java b/src/main/java/eu/hbp/mip/repositories/DatasetRepository.java deleted file mode 100644 index 7c6da1e97..000000000 --- a/src/main/java/eu/hbp/mip/repositories/DatasetRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package eu.hbp.mip.repositories; - -import eu.hbp.mip.model.Dataset; -import org.springframework.data.repository.CrudRepository; - -/** - * Created by mirco on 11.07.16. - */ - -public interface DatasetRepository extends CrudRepository<Dataset, String> { -} diff --git a/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java b/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java index 2f6ae857d..eb1d25415 100644 --- a/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java +++ b/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java @@ -1,6 +1,6 @@ package eu.hbp.mip.repositories; -import eu.hbp.mip.model.Experiment; +import eu.hbp.mip.model.DAOs.ExperimentDAO; import eu.hbp.mip.model.User; import org.springframework.data.repository.CrudRepository; @@ -10,8 +10,8 @@ import java.util.UUID; * Created by mirco on 11.07.16. */ -public interface ExperimentRepository extends CrudRepository<Experiment, UUID> { - Iterable<Experiment> findByCreatedBy(User user); +public interface ExperimentRepository extends CrudRepository<ExperimentDAO, UUID> { + Iterable<ExperimentDAO> findByCreatedBy(User user); - Iterable<Experiment> findByShared(Boolean shared); + Iterable<ExperimentDAO> findByShared(Boolean shared); } diff --git a/src/main/java/eu/hbp/mip/repositories/ModelRepository.java b/src/main/java/eu/hbp/mip/repositories/ModelRepository.java deleted file mode 100644 index c25b1e5bb..000000000 --- a/src/main/java/eu/hbp/mip/repositories/ModelRepository.java +++ /dev/null @@ -1,17 +0,0 @@ -package eu.hbp.mip.repositories; - -import eu.hbp.mip.model.Model; -import eu.hbp.mip.model.User; -import org.springframework.data.repository.CrudRepository; - -/** - * Created by mirco on 11.07.16. - */ - -public interface ModelRepository extends CrudRepository<Model, String> { - Long countByTitle(String Title); - - Iterable<Model> findByCreatedByOrderByCreatedAt(User user); - - Iterable<Model> findByValidOrCreatedByOrderByCreatedAt(Boolean valid, User user); -} diff --git a/src/main/java/eu/hbp/mip/repositories/QueryRepository.java b/src/main/java/eu/hbp/mip/repositories/QueryRepository.java deleted file mode 100644 index f0b95d39c..000000000 --- a/src/main/java/eu/hbp/mip/repositories/QueryRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package eu.hbp.mip.repositories; - -import eu.hbp.mip.model.Query; -import org.springframework.data.repository.CrudRepository; - -/** - * Created by mirco on 11.07.16. - */ - -public interface QueryRepository extends CrudRepository<Query, Long> { -} diff --git a/src/main/java/eu/hbp/mip/repositories/TagRepository.java b/src/main/java/eu/hbp/mip/repositories/TagRepository.java deleted file mode 100644 index 4edb4090e..000000000 --- a/src/main/java/eu/hbp/mip/repositories/TagRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package eu.hbp.mip.repositories; - -import eu.hbp.mip.model.Tag; -import org.springframework.data.repository.CrudRepository; - -/** - * Created by mirco on 11.07.16. - */ - -public interface TagRepository extends CrudRepository<Tag, String> { -} diff --git a/src/main/java/eu/hbp/mip/repositories/ValueRepository.java b/src/main/java/eu/hbp/mip/repositories/ValueRepository.java deleted file mode 100644 index b41574d60..000000000 --- a/src/main/java/eu/hbp/mip/repositories/ValueRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package eu.hbp.mip.repositories; - -import eu.hbp.mip.model.Value; -import org.springframework.data.repository.CrudRepository; - -/** - * Created by mirco on 11.07.16. - */ - -public interface ValueRepository extends CrudRepository<Value, String> { -} diff --git a/src/main/java/eu/hbp/mip/repositories/VariableRepository.java b/src/main/java/eu/hbp/mip/repositories/VariableRepository.java deleted file mode 100644 index 3ded94943..000000000 --- a/src/main/java/eu/hbp/mip/repositories/VariableRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package eu.hbp.mip.repositories; - -import eu.hbp.mip.model.Variable; -import org.springframework.data.repository.CrudRepository; - -/** - * Created by mirco on 13.09.16. - */ -public interface VariableRepository extends CrudRepository<Variable, String> { -} diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java new file mode 100644 index 000000000..bd0bed359 --- /dev/null +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -0,0 +1,776 @@ +package eu.hbp.mip.services; + +import com.github.jmchilton.blend4j.galaxy.GalaxyInstance; +import com.github.jmchilton.blend4j.galaxy.GalaxyInstanceFactory; +import com.github.jmchilton.blend4j.galaxy.WorkflowsClient; +import com.github.jmchilton.blend4j.galaxy.beans.Workflow; +import com.github.jmchilton.blend4j.galaxy.beans.WorkflowDetails; +import com.github.jmchilton.blend4j.galaxy.beans.WorkflowInputDefinition; +import com.google.common.collect.Lists; +import com.google.gson.*; +import eu.hbp.mip.controllers.galaxy.retrofit.RetroFitGalaxyClients; +import eu.hbp.mip.controllers.galaxy.retrofit.RetrofitClientInstance; +import eu.hbp.mip.model.DAOs.ExperimentDAO; +import eu.hbp.mip.model.DTOs.AlgorithmDTO; +import eu.hbp.mip.model.DTOs.ExperimentDTO; +import eu.hbp.mip.model.User; +import eu.hbp.mip.model.UserInfo; +import eu.hbp.mip.model.galaxy.ErrorResponse; +import eu.hbp.mip.model.galaxy.GalaxyWorkflowResult; +import eu.hbp.mip.model.galaxy.PostWorkflowToGalaxyDtoResponse; +import eu.hbp.mip.repositories.ExperimentRepository; +import eu.hbp.mip.utils.ClaimUtils; +import eu.hbp.mip.utils.HTTPUtil; +import eu.hbp.mip.utils.JsonConverters; +import eu.hbp.mip.utils.Logging; +import org.codehaus.jettison.json.JSONException; +import org.codehaus.jettison.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Service; +import retrofit2.Call; +import retrofit2.Response; + +import java.io.IOException; +import java.util.*; + +import static java.lang.Thread.sleep; + +@Service +public class ExperimentService { + + @Autowired + private UserInfo userInfo; + + private static final Gson gsonOnlyExposed = new GsonBuilder().serializeNulls() + .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").excludeFieldsWithoutExposeAnnotation().create(); + + @Value("#{'${services.exareme.queryExaremeUrl}'}") + private String queryExaremeUrl; + + @Value("#{'${services.workflows.workflowUrl}'}") + private String workflowUrl; + + @Value("#{'${services.workflows.jwtSecret}'}") + private String jwtSecret; + + @Value("#{'${services.galaxy.galaxyUrl}'}") + private String galaxyUrl; + + @Value("#{'${services.galaxy.galaxyApiKey}'}") + private String galaxyApiKey; + + // Enable HBP collab authentication (1) or disable it (0). Default is 1 + @Value("#{'${hbp.authentication.enabled:1}'}") + private boolean authenticationIsEnabled; + + @Autowired + private ExperimentRepository experimentRepository; + + private static final Gson gson = new Gson(); + + public ResponseEntity<String> getExperiment(String uuid, String endpoint) { + + ExperimentDAO experimentDAO; + User user = userInfo.getUser(); + + Logging.LogUserAction(user.getUsername(), endpoint, "Loading Experiment with uuid : " + uuid); + + experimentDAO = loadExperiment(uuid); + + if (experimentDAO == null) { + Logging.LogUserAction(user.getUsername(), endpoint, "Experiment Not found."); + return new ResponseEntity<>("Not found", HttpStatus.NOT_FOUND); + } + + if (!experimentDAO.isShared() && !experimentDAO.getCreatedBy().getUsername().equals(user.getUsername())) { + Logging.LogUserAction(user.getUsername(), endpoint, "Accessing Experiment is unauthorized."); + return new ResponseEntity<>("You don't have access to the experiment.", HttpStatus.UNAUTHORIZED); + } + ExperimentDTO experimentDTO = experimentDAO.convertToDTO(); + Logging.LogUserAction(user.getUsername(), endpoint, "Experiment was Loaded with uuid : " + uuid + "."); + + return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); + } + + public ResponseEntity<String> createExperiment(Authentication authentication, ExperimentDTO experimentDTO, String endpoint) { + User user = userInfo.getUser(); + + //Checking if check (POST) /experiments has proper input. + if (checkPostExperimentProperInput(experimentDTO)){ + Logging.LogUserAction(user.getUsername(), endpoint, + "Invalid input."); + return ResponseEntity.badRequest().body("Please provide proper input."); + } + // Get the type and name of algorithm + String algorithmType = experimentDTO.getAlgorithm().getType(); + String algorithmName = experimentDTO.getAlgorithm().getName(); + + StringBuilder parametersLogMessage = new StringBuilder(", Parameters:\n"); + for (AlgorithmDTO.AlgorithmParamDTO params : experimentDTO.getAlgorithm().getParameters()) { + parametersLogMessage + .append(" ") + .append(params.getLabel()) + .append(" -> ") + .append(params.getValue()) + .append("\n"); + } + Logging.LogUserAction(user.getUsername(), endpoint, "Executing " + algorithmName + parametersLogMessage); + + if (authenticationIsEnabled) { + // Getting the dataset from the experiment parameters + String experimentDatasets = null; + for (AlgorithmDTO.AlgorithmParamDTO parameter : experimentDTO.getAlgorithm().getParameters()) { + if (parameter.getLabel().equals("dataset")) { + experimentDatasets = parameter.getValue(); + break; + } + } + + if (experimentDatasets == null || experimentDatasets.equals("")) { + Logging.LogUserAction(user.getUsername(), endpoint, + "A dataset should be specified to run an algorithm."); + return ResponseEntity.badRequest().body("Please provide at least one dataset to run the algorithm."); + } + + // --- Validating proper access rights on the datasets --- + if (!ClaimUtils.userHasDatasetsAuthorization(user.getUsername(), authentication.getAuthorities(), experimentDatasets)) { + return ResponseEntity.badRequest().body("You are not authorized to use these datasets."); + } + } + + // Run with the appropriate engine + if (algorithmType.equals("workflow")) { + Logging.LogUserAction(user.getUsername(), endpoint, "Algorithm runs on Galaxy."); + return runGalaxyWorkflow(experimentDTO, endpoint); + } else { + Logging.LogUserAction(user.getUsername(), endpoint, "Algorithm runs on Exareme."); + return runExaremeAlgorithm(experimentDTO, endpoint); + } + } + + public ResponseEntity<String> getExperiments( String endpoint) { + User user = userInfo.getUser(); + Logging.LogUserAction(user.getUsername(), endpoint, "Listing my experiments."); + + Iterable<ExperimentDAO> myExperiments = experimentRepository.findByCreatedBy(user); + List<ExperimentDAO> expList = Lists.newLinkedList(myExperiments); + Iterable<ExperimentDAO> sharedExperiments = experimentRepository.findByShared(true); + List<ExperimentDAO> sharedExpList = Lists.newLinkedList(sharedExperiments); + expList.addAll(sharedExpList); + Logging.LogUserAction(user.getUsername(), endpoint, "Successfully listed my experiments."); + List<ExperimentDTO> experimentDTOs = new LinkedList<>(); + + for (ExperimentDAO experimentDAO: expList){ + ExperimentDTO experimentDTO = experimentDAO.convertToDTO(); + experimentDTOs.add(experimentDTO); + } + return new ResponseEntity<>(new Gson().toJson(experimentDTOs ), HttpStatus.OK); + } + + public ResponseEntity<String> updateExperiment(String uuid, ExperimentDTO experimentDTO, String endpoint) + { + ExperimentDAO experimentDAO; + User user = userInfo.getUser(); + Logging.LogUserAction(user.getUsername(), endpoint, "Updating experiment with uuid : " + experimentDTO.getUuid() + "."); + //Checking if check (PUT) /experiments has proper input. + if (checkPutExperimentProperInput(experimentDTO)){ + Logging.LogUserAction(user.getUsername(), endpoint, + "Invalid input."); + return ResponseEntity.badRequest().body("Please provide proper input."); + } + + if((experimentDTO.getName() == null || experimentDTO.getName().length() == 0) + && experimentDTO.getShared() == null + && experimentDTO.getViewed() == null + && experimentDTO.getAlgorithm() == null) + { + return ResponseEntity.badRequest().body("Input is required."); + } + + experimentDAO = loadExperiment(uuid); + + if (!experimentDAO.getCreatedBy().getUsername().equals(user.getUsername())) + return new ResponseEntity<>("You're not the owner of this experiment", HttpStatus.UNAUTHORIZED); + + if(experimentDTO.getName() != null && experimentDTO.getName().length() != 0) + { + experimentDAO.setName(experimentDTO.getName()); + } + + if(experimentDTO.getShared() != null) + { + experimentDAO.setShared(experimentDTO.getShared()); + } + + if(experimentDTO.getViewed() != null) + { + experimentDAO.setViewed(experimentDTO.getViewed()); + } + + experimentRepository.save(experimentDAO); + + Logging.LogUserAction(user.getUsername(), endpoint, "Updated experiment with uuid : " + experimentDTO.getUuid() + "."); + + experimentDTO = experimentDAO.convertToDTO(); + return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); + } + + public ResponseEntity<String> deleteExperiment(String uuid, String endpoint) + { + ExperimentDAO experimentDAO; + User user = userInfo.getUser(); + Logging.LogUserAction(user.getUsername(), endpoint, "Deleting experiment with uuid : " + uuid + "."); + + experimentDAO = loadExperiment(uuid); + + if (!experimentDAO.getCreatedBy().getUsername().equals(user.getUsername())) + return new ResponseEntity<>(endpoint, HttpStatus.UNAUTHORIZED); + + experimentRepository.delete(experimentDAO); + + Logging.LogUserAction(user.getUsername(), endpoint, "Deleted experiment with uuid : " + uuid + "."); + return new ResponseEntity<>(HttpStatus.OK); + } + + // /* ------------------------------- PRIVATE METHODS ----------------------------------------------------*/ + private boolean checkPostExperimentProperInput(ExperimentDTO experimentDTO) + { + return experimentDTO.getShared() != null + || experimentDTO.getViewed() != null + || experimentDTO.getCreated() != null + || experimentDTO.getCreatedBy() != null + || experimentDTO.getResult() != null + || experimentDTO.getStatus() != null + || experimentDTO.getUuid() != null; + } + + private boolean checkPutExperimentProperInput(ExperimentDTO experimentDTO) + { + return experimentDTO.getUuid() != null + || experimentDTO.getCreated() != null + || experimentDTO.getResult() != null + || experimentDTO.getStatus() != null; + } + + private ExperimentDAO loadExperiment(String uuid){ + + UUID experimentUuid ; + + try { + experimentUuid = UUID.fromString(uuid); + } catch (IllegalArgumentException iae) { + return null; + } + + return experimentRepository.findOne(experimentUuid); + } + + private ExperimentDAO createExperiment(ExperimentDTO experimentDTO, String endpoint) { + User user = userInfo.getUser(); + + ExperimentDAO experimentDAO = new ExperimentDAO(); + experimentDAO.setUuid(UUID.randomUUID()); + experimentDAO.setCreatedBy(user); + experimentDAO.setAlgorithm(JsonConverters.convertObjectToJsonString(experimentDTO.getAlgorithm())); + experimentDAO.setName(experimentDTO.getName()); + experimentRepository.save(experimentDAO); + + Logging.LogUserAction(user.getUsername(), endpoint, " id : " + experimentDAO.getUuid()); + Logging.LogUserAction(user.getUsername(), endpoint, " algorithms : " + experimentDAO.getAlgorithm()); + Logging.LogUserAction(user.getUsername(), endpoint, " name : " + experimentDAO.getName()); + return experimentDAO; + } + + private void saveExperiment(ExperimentDAO experimentDAO, String endpoint) { + User user = userInfo.getUser(); + + Logging.LogUserAction(user.getUsername(), endpoint, " id : " + experimentDAO.getUuid()); + Logging.LogUserAction(user.getUsername(), endpoint, " algorithms : " + experimentDAO.getAlgorithm()); + Logging.LogUserAction(user.getUsername(), endpoint, " name : " + experimentDAO.getName()); + Logging.LogUserAction(user.getUsername(), endpoint, " historyId : " + experimentDAO.getWorkflowHistoryId()); + Logging.LogUserAction(user.getUsername(), endpoint, " status : " + experimentDAO.getStatus()); + + experimentRepository.save(experimentDAO); + + Logging.LogUserAction(user.getUsername(), endpoint, "Saved experiment"); + } + + private void finishExperiment(ExperimentDAO experimentDAO) { + experimentDAO.setFinished(new Date()); + experimentRepository.save(experimentDAO); + } + + /* -------------------------------------- EXAREME CALLS ---------------------------------------------------------*/ + + /** + * The runExaremeExperiment will POST the algorithm to the exareme client + * + * @param experimentDTO is the request with the experiment information + * @return the response to be returned + */ + public ResponseEntity<String> runExaremeAlgorithm(ExperimentDTO experimentDTO, String endpoint) { + User user = userInfo.getUser(); + Logging.LogUserAction(user.getUsername(), endpoint, "Running the algorithm..."); + + ExperimentDAO experimentDAO = createExperiment(experimentDTO, endpoint); + Logging.LogUserAction(user.getUsername(), endpoint, "Created experiment with uuid :" + experimentDAO.getUuid()); + + // Run the 1st algorithm from the list + String algorithmName = experimentDTO.getAlgorithm().getName(); + + // Get the parameters + List<AlgorithmDTO.AlgorithmParamDTO> algorithmParameters + = experimentDTO.getAlgorithm().getParameters(); + + String body = gson.toJson(algorithmParameters); + String url = queryExaremeUrl + "/" + algorithmName; + Logging.LogUserAction(user.getUsername(), endpoint, "url: " + url + ", body: " + body); + + Logging.LogUserAction(user.getUsername(), endpoint, + "Completed, returning: " + experimentDTO.toString()); + + Logging.LogUserAction(user.getUsername(), endpoint, + "Starting exareme execution thread"); + new Thread(() -> { + // ATTENTION: Inside the Thread only LogExperimentAction should be used, not LogUserAction! + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Thread named :" + Thread.currentThread().getName() + " with id :" + Thread.currentThread().getId() + " started!"); + + try { + StringBuilder results = new StringBuilder(); + int code = HTTPUtil.sendPost(url, body, results); + + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Algorithm finished with code: " + code); + + // Results are stored in the experiment object + ExperimentDTO.ResultDTO resultDTO = JsonConverters.convertJsonStringToObject(String.valueOf(results), ExperimentDTO.ResultDTO.class); + + experimentDAO.setResult(JsonConverters.convertObjectToJsonString(resultDTO)); +// experimentDAO.setHasError(code >= 400); +// experimentDAO.setHasServerError(code >= 500); + } catch (Exception e) { + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "There was an exception: " + e.getMessage()); + + experimentDAO.setStatus(ExperimentDAO.Status.error); + experimentDAO.setResult(e.getMessage()); + } + + finishExperiment(experimentDAO); + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Finished the experiment: " + experimentDAO.toString()); + }).start(); + experimentDTO = experimentDAO.convertToDTO(); + return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); + } + + /* --------------------------------------- GALAXY CALLS ---------------------------------------------------------*/ + + + /** + * The runWorkflow will POST the algorithm to the galaxy client + * + * @param experimentDTO is the request with the experiment information + * @return the response to be returned + */ + public ResponseEntity<String> runGalaxyWorkflow(ExperimentDTO experimentDTO, String endpoint) { + User user = userInfo.getUser(); + Logging.LogUserAction(user.getUsername(), endpoint, "Running a workflow..."); + + ExperimentDAO experimentDAO = createExperiment(experimentDTO, endpoint); + Logging.LogUserAction(user.getUsername(), endpoint, "Created experiment with uuid :" + experimentDAO.getUuid()); + + + // Run the 1st algorithm from the list + String workflowId = experimentDTO.getAlgorithm().getName(); + + // Get the parameters + List<AlgorithmDTO.AlgorithmParamDTO> algorithmParameters + = experimentDTO.getAlgorithm().getParameters(); + + // Convert the parameters to workflow parameters + HashMap<String, String> algorithmParamsIncludingEmpty = new HashMap<>(); + if (algorithmParameters != null) { + for (AlgorithmDTO.AlgorithmParamDTO param : algorithmParameters) { + algorithmParamsIncludingEmpty.put(param.getName(), param.getValue()); + } + } + + // Get all the algorithm parameters because the frontend provides only the non-null + final GalaxyInstance instance = GalaxyInstanceFactory.get(galaxyUrl, galaxyApiKey); + final WorkflowsClient workflowsClient = instance.getWorkflowsClient(); + Workflow workflow = null; + for (Workflow curWorkflow : workflowsClient.getWorkflows()) { + if (curWorkflow.getId().equals(workflowId)) { + workflow = curWorkflow; + break; + } + } + if (workflow == null) { + Logging.LogUserAction(user.getUsername(), endpoint, + "Could not find algorithm code: " + workflowId); + return ResponseEntity.badRequest() + .body(new ErrorResponse("Could not find galaxy algorithm.").toString()); + } + final WorkflowDetails workflowDetails = workflowsClient.showWorkflow(workflow.getId()); + for (Map.Entry<String, WorkflowInputDefinition> workflowParameter : workflowDetails.getInputs().entrySet()) { + if (!(algorithmParamsIncludingEmpty.containsKey(workflowParameter.getValue().getUuid()))) { + algorithmParamsIncludingEmpty.put(workflowParameter.getValue().getUuid(), ""); + } + } + + // Create the body of the request + HashMap<String, HashMap<String, String>> requestBody = new HashMap<>(); + requestBody.put("inputs", algorithmParamsIncludingEmpty); + JsonObject requestBodyJson = new JsonParser().parse(gson.toJson(requestBody)).getAsJsonObject(); + + // Create the request client + RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); + Logging.LogUserAction(user.getUsername(), endpoint, "Running Galaxy workflow with id: " + workflow.getId()); + + // Call Galaxy to run the workflow + Call<PostWorkflowToGalaxyDtoResponse> call = service.postWorkflowToGalaxy(workflow.getId(), galaxyApiKey, requestBodyJson); + try { + Response<PostWorkflowToGalaxyDtoResponse> response = call.execute(); + + if (response.code() == 200) { // Call succeeded + String responseBody = gson.toJson(response.body()); + Logging.LogUserAction(user.getUsername(), endpoint, "Response: " + responseBody); + + String historyId = (String) new JSONObject(responseBody).get("history_id"); + experimentDAO.setWorkflowHistoryId(historyId); + experimentDAO.setStatus(ExperimentDAO.Status.pending); +// experimentDAO.setHasServerError(response.code() >= 500); + + } else { // Something unexpected happened + String msgErr = gson.toJson(response.errorBody()); + Logging.LogUserAction(user.getUsername(), endpoint, "Error Response: " + msgErr); + + // Values are read from streams. + JSONObject jObjectError = new JSONObject(msgErr); + String errMsg = jObjectError.get("err_msg").toString(); + + experimentDAO.setResult(errMsg); +// experimentDAO.setHasError(response.code() >= 400); +// experimentDAO.setHasServerError(response.code() >= 500); + } + + } catch (Exception e) { + Logging.LogUserAction(user.getUsername(), endpoint, "An exception occurred: " + e.getMessage()); + experimentDAO.setStatus(ExperimentDAO.Status.error); + experimentDAO.setResult(e.getMessage()); + } + saveExperiment(experimentDAO, endpoint); + + // Start the process of fetching the status + updateWorkflowExperiment(experimentDAO, endpoint); + + Logging.LogUserAction(user.getUsername(), endpoint, "Run workflow completed!"); + + experimentDTO = experimentDAO.convertToDTO(); + return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); + } + + + /** + * This method creates a thread that will fetch the workflow result when it is ready + * + * @param experimentDAO will be used to fetch it's workflow status, it should have the workflowHistoryId initialized + * and the result should not already be fetched + * @return nothing, just updates the experiment + */ + public void updateWorkflowExperiment(ExperimentDAO experimentDAO, String endpoint) { + User user = userInfo.getUser(); + + if (experimentDAO == null) { + Logging.LogUserAction(user.getUsername(), endpoint, "The experiment does not exist."); + return; + } + + Logging.LogUserAction(user.getUsername(), endpoint, + " Experiment id : " + experimentDAO.getUuid()); + if (experimentDAO.getWorkflowHistoryId() == null) { + Logging.LogUserAction(user.getUsername(), endpoint, "History Id does not exist."); + return; + } + + Logging.LogUserAction(user.getUsername(), endpoint, "Starting Thread..."); + new Thread(() -> { + while (true) { + // ATTENTION: Inside the Thread only LogExperimentAction should be used, not LogExperimentAction! + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Thread is running..."); + + try { + sleep(2000); + } catch (InterruptedException e) { + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Sleep was disrupted: " + e.getMessage()); + } + + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Fetching status for experiment Id: " + experimentDAO.getUuid()); + + String state = getWorkflowStatus(experimentDAO); + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "State is: " + state); + + switch (state) { + case "running": + // Do nothing, when the experiment is created the status is set to running + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Workflow is still running."); + break; + + case "completed": + // Get only the job result that is visible + List<GalaxyWorkflowResult> workflowJobsResults = getWorkflowResults(experimentDAO); + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Results are: " + workflowJobsResults.toString()); + + boolean resultFound = false; + for (GalaxyWorkflowResult jobResult : workflowJobsResults) { + if (jobResult.getVisible()) { + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Visible result are: " + jobResult.getId()); + + String result = getWorkflowResultBody(experimentDAO, jobResult.getId()); + + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "ResultDTO: " + result); + if (result == null) { + experimentDAO.setStatus(ExperimentDAO.Status.error); + } + experimentDAO.setResult("[" + result + "]"); + experimentDAO.setStatus(ExperimentDAO.Status.success); + resultFound = true; + } + } + + if (!resultFound) { // If there is no visible result + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "No visible result"); + experimentDAO.setResult("[" + new ErrorResponse("The workflow has no visible result.").toString() + "]"); + experimentDAO.setStatus(ExperimentDAO.Status.error); + } + + finishExperiment(experimentDAO); + break; + + case "error": + // Get the job result that failed + workflowJobsResults = getWorkflowResults(experimentDAO); + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Error results are: " + workflowJobsResults.toString()); + + boolean failedJobFound = false; + for (GalaxyWorkflowResult jobResult : workflowJobsResults) { + if (jobResult.getState().equals("error")) { + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Failed job is: " + jobResult.getId()); + + String result = getWorkflowJobError(jobResult.getId(), experimentDAO); + + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Job result: " + result); + if (result == null) { + experimentDAO.setStatus(ExperimentDAO.Status.error); + } + experimentDAO.setResult("[" + result + "]"); + experimentDAO.setStatus(ExperimentDAO.Status.error); + failedJobFound = true; + } + } + + if (!failedJobFound) { // If there is no visible failed job + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "No failed result"); + experimentDAO.setResult("[" + new ErrorResponse("The workflow has no failed result.").toString() + "]"); + experimentDAO.setStatus(ExperimentDAO.Status.error); + } + finishExperiment(experimentDAO); + break; + + default: // InternalError or unexpected result + experimentDAO.setResult("[" + new ErrorResponse("An unexpected error occurred.").toString() + "]"); + experimentDAO.setStatus(ExperimentDAO.Status.error); + finishExperiment(experimentDAO); + break; + } + + // If result exists return + if (experimentDAO.getResult() != null) { + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "ResultDTO exists: " + experimentDAO.getResult()); + return; + } + } + }).start(); + } + + + /** + * @param experimentDAO The experiment of the workflow + * @return "running" -> When the workflow is still running + * "internalError" -> When an exception or a bad request occurred + * "error" -> When the workflow produced an error + * "completed" -> When the workflow completed successfully + */ + public String getWorkflowStatus(ExperimentDAO experimentDAO) { + String historyId = experimentDAO.getWorkflowHistoryId(); + String experimentName = experimentDAO.getName(); + UUID experimentId = experimentDAO.getUuid(); + + // ATTENTION: This function is used from a Thread. Only LogExperimentAction should be used, not LogUserAction! + Logging.LogExperimentAction(experimentName, experimentId, " History Id : " + historyId); + + // Create the request client + RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); + Call<Object> call = service.getWorkflowStatusFromGalaxy(historyId, galaxyApiKey); + + String result = null; + try { + Response<Object> response = call.execute(); + if (response.code() >= 400) { + Logging.LogExperimentAction(experimentName, experimentId, " Response code: " + + response.code() + "" + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); + return "internalError"; + } + result = new Gson().toJson(response.body()); + Logging.LogExperimentAction(experimentName, experimentId, " ResultDTO: " + result); + + } catch (IOException e) { + Logging.LogExperimentAction(experimentName, experimentId, " An exception happened: " + e.getMessage()); + return "internalError"; + } + + String state = null; + try { + JSONObject resultJson = new JSONObject(result); + state = resultJson.getString("state"); + } catch (JSONException e) { + Logging.LogExperimentAction(experimentName, experimentId, " An exception happened: " + e.getMessage()); + return "internalError"; + } + + Logging.LogExperimentAction(experimentName, experimentId, " Completed!"); + switch (state) { + case "ok": + return "completed"; + case "error": + return "error"; + case "running": + case "new": + case "waiting": + case "queued": + return "running"; + default: + return "internalError"; + } + } + + /** + * @param experimentDAO The experiment of the workflow + * @return a List<GalaxyWorkflowResult> or null when an error occurred + */ + public List<GalaxyWorkflowResult> getWorkflowResults(ExperimentDAO experimentDAO) { + + String historyId = experimentDAO.getWorkflowHistoryId(); + String experimentName = experimentDAO.getName(); + UUID experimentId = experimentDAO.getUuid(); + Logging.LogExperimentAction(experimentName, experimentId, " historyId : " + historyId); + + RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); + Call<List<GalaxyWorkflowResult>> call = service.getWorkflowResultsFromGalaxy(historyId, galaxyApiKey); + + List<GalaxyWorkflowResult> getGalaxyWorkflowResultList = null; + try { + Response<List<GalaxyWorkflowResult>> response = call.execute(); + if (response.code() >= 400) { + Logging.LogExperimentAction(experimentName, experimentId, " Response code: " + + response.code() + "" + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); + return null; + } + getGalaxyWorkflowResultList = response.body(); + Logging.LogExperimentAction(experimentName, experimentId, " ResultDTO: " + response.body()); + + } catch (IOException e) { + Logging.LogExperimentAction(experimentName, experimentId, " An exception happened: " + e.getMessage()); + return null; + } + + Logging.LogExperimentAction(experimentName, experimentId, " Completed!"); + return getGalaxyWorkflowResultList; + + } + + /** + * @param experimentDAO The experiment of the workflow + * @param contentId the id of the job result that we want + * @return the result of the specific workflow job, null if there was an error + */ + public String getWorkflowResultBody(ExperimentDAO experimentDAO, String contentId) { + + String historyId = experimentDAO.getWorkflowHistoryId(); + String experimentName = experimentDAO.getName(); + UUID experimentId = experimentDAO.getUuid(); + + Logging.LogExperimentAction(experimentName, experimentId, " historyId : " + historyId); + + RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); + Call<Object> call = + service.getWorkflowResultsBodyFromGalaxy(historyId, contentId, galaxyApiKey); + + String resultJson = null; + try { + Response<Object> response = call.execute(); + if (response.code() >= 400) { + Logging.LogExperimentAction(experimentName, experimentId, " Response code: " + + response.code() + "" + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); + return null; + } + resultJson = new Gson().toJson(response.body()); + Logging.LogExperimentAction(experimentName, experimentId, " ResultDTO: " + resultJson); + + } catch (IOException e) { + Logging.LogExperimentAction(experimentName, experimentId, + " An exception happened: " + e.getMessage()); + return null; + } + + Logging.LogExperimentAction(experimentName, experimentId, " Completed!"); + return resultJson; + } + + + /** + * @param jobId the id of the workflow job that failed + * @return the error that was produced or null if an error occurred + */ + public String getWorkflowJobError(String jobId, ExperimentDAO experimentDAO) { + String experimentName = experimentDAO.getName(); + UUID experimentId = experimentDAO.getUuid(); + + Logging.LogExperimentAction(experimentName, experimentId, " jobId : " + jobId); + RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); + Call<Object> callError = service.getErrorMessageOfWorkflowFromGalaxy(jobId, galaxyApiKey); + + String fullError = null; + String returnError = null; + try { + Response<Object> response = callError.execute(); + if (response.code() >= 400) { + Logging.LogExperimentAction(experimentName, experimentId, "Response code: " + + response.code() + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); + return null; + } + + // Parsing the stderr of the job that failed + String jsonString = new Gson().toJson(response.body()); + JsonElement jsonElement = new JsonParser().parse(jsonString); + JsonObject rootObject = jsonElement.getAsJsonObject(); + fullError = rootObject.get("stderr").getAsString(); + Logging.LogExperimentAction(experimentName, experimentId, "Error: " + fullError); + + String[] arrOfStr = fullError.split("ValueError", 0); + String specError = arrOfStr[arrOfStr.length - 1]; + returnError = specError.substring(1); + Logging.LogExperimentAction(experimentName, experimentId, "Parsed Error: " + returnError); + + } catch (IOException e) { + Logging.LogExperimentAction(experimentName, experimentId, "Exception: " + e.getMessage()); + return null; + } + + Logging.LogExperimentAction(experimentName, experimentId, "Completed successfully!"); + + return returnError; + } +} diff --git a/src/main/java/eu/hbp/mip/utils/ClaimUtils.java b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java index e71d3f190..5872819bc 100644 --- a/src/main/java/eu/hbp/mip/utils/ClaimUtils.java +++ b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java @@ -1,7 +1,7 @@ package eu.hbp.mip.utils; import com.google.gson.Gson; -import eu.hbp.mip.model.PathologyDTO; +import eu.hbp.mip.model.DTOs.PathologyDTO; import org.springframework.security.core.GrantedAuthority; import java.util.ArrayList; diff --git a/src/main/java/eu/hbp/mip/utils/JsonConverters.java b/src/main/java/eu/hbp/mip/utils/JsonConverters.java new file mode 100644 index 000000000..52305124a --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/JsonConverters.java @@ -0,0 +1,27 @@ +package eu.hbp.mip.utils; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; + +import java.lang.reflect.Type; + +public class JsonConverters { + Gson gson = new Gson(); + + public static String convertObjectToJsonString(Object object) { + ObjectMapper mapper = new ObjectMapper(); + //Converting the Object to JSONString + try { + return mapper.writeValueAsString(object); + } catch (JsonProcessingException e) { + return e.getMessage(); + } + } + + public static <T> T convertJsonStringToObject(String jsonString, Type typeOfT) { + if(jsonString == null || jsonString.isEmpty()) + return null; + return new Gson().fromJson(jsonString, typeOfT); + } +} diff --git a/src/main/resources/db/migration/V7_0__NewExperimentStructure.sql b/src/main/resources/db/migration/V7_0__NewExperimentStructure.sql new file mode 100644 index 000000000..b69ae2b70 --- /dev/null +++ b/src/main/resources/db/migration/V7_0__NewExperimentStructure.sql @@ -0,0 +1,45 @@ +ALTER TABLE experiment +DROP COLUMN haserror, +DROP COLUMN hasservererror, +DROP COLUMN validations, +DROP COLUMN model_slug; + +ALTER TABLE experiment +RENAME algorithms TO algorithm; +ALTER TABLE experiment +RENAME createdby_username TO created_by_username; +ALTER TABLE experiment +RENAME workflowhistoryid TO workflow_history_id; +ALTER TABLE experiment +RENAME resultsviewed TO viewed; +ALTER TABLE experiment +RENAME workflowstatus TO status; + +ALTER TABLE "user" +DROP COLUMN birthday, +DROP COLUMN city, +DROP COLUMN country, +DROP COLUMN firstname, +DROP COLUMN gender, +DROP COLUMN isactive, +DROP COLUMN lastname, +DROP COLUMN password, +DROP COLUMN phone, +DROP COLUMN picture, +DROP COLUMN team, +DROP COLUMN web; + +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 "variable_value"; +DROP TABLE "query_training_datasets", "query_validation_datasets", "query_testing_datasets"; +DROP TABLE "variable", "value"; +DROP TABLE "group_group", "group"; +DROP TABLE "model"; +DROP TABLE "query"; +DROP TABLE "dataset"; +DROP TABLE "config"; +DROP TABLE "vote", "app"; +DROP TABLE "user_roles", "user_languages"; diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 0726d82f4..eb9ace1a6 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -10,8 +10,7 @@ <logger name="eu.hbp.mip"> <appender-ref ref="FILE1" /> </logger> - <include resource="org/springframework/boot/logging/logback/base.xml"/> - <logger name="org.springframework.web" level="ERROR"> + <logger name="org.springframework.web" level="INFO"> <appender-ref ref="FILE1" /> </logger> <logger name="eu.hbp.mip.utils" level="INFO" additivity="false"> -- GitLab From 3d25810a35ca939182d4b1d1842942f0ca250ffb Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Fri, 6 Nov 2020 01:36:08 -0800 Subject: [PATCH 02/52] Merge with elastic logs. --- docker/config/application.tmpl | 2 +- src/main/resources/logback.xml | 19 ------------------- 2 files changed, 1 insertion(+), 20 deletions(-) delete mode 100644 src/main/resources/logback.xml diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index 6174fb409..ae9d0f8fd 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -53,7 +53,7 @@ logging: root: {{ default .Env.LOG_LEVEL "INFO" }} org: springframework: - security: DEBUG + security: INFO web: {{ default .Env.LOGGING_LEVEL_WEB "WARN" }} web.servlet.handler.BeanNameUrlHandlerMapping: WARN hibernate: {{ default .Env.LOGGING_LEVEL_HIBERNATE "WARN" }} diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml deleted file mode 100644 index eb9ace1a6..000000000 --- a/src/main/resources/logback.xml +++ /dev/null @@ -1,19 +0,0 @@ -<configuration> - <appender name="FILE1" class="ch.qos.logback.core.FileAppender"> - <file>logs/portal-backend.txt</file> - <append>true</append> - <encoder> - <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %msg%n</pattern> - </encoder> - </appender> - - <logger name="eu.hbp.mip"> - <appender-ref ref="FILE1" /> - </logger> - <logger name="org.springframework.web" level="INFO"> - <appender-ref ref="FILE1" /> - </logger> - <logger name="eu.hbp.mip.utils" level="INFO" additivity="false"> - <appender-ref ref="FILE1" /> - </logger> -</configuration> \ No newline at end of file -- GitLab From c899bb9586c7707c7308f87a29425935d10477fa Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Tue, 20 Oct 2020 14:15:31 +0300 Subject: [PATCH 03/52] Limit logs from spring framework. --- docker/config/application.tmpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index ae9d0f8fd..dce1ac742 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -53,10 +53,10 @@ logging: root: {{ default .Env.LOG_LEVEL "INFO" }} org: springframework: - security: INFO - web: {{ default .Env.LOGGING_LEVEL_WEB "WARN" }} - web.servlet.handler.BeanNameUrlHandlerMapping: WARN - hibernate: {{ default .Env.LOGGING_LEVEL_HIBERNATE "WARN" }} + security: ERROR + web: {{ default .Env.LOGGING_LEVEL_WEB "ERROR" }} + web.servlet.handler.BeanNameUrlHandlerMapping: ERROR + hibernate: {{ default .Env.LOGGING_LEVEL_HIBERNATE "ERROR" }} eu: hbp: {{ default .Env.LOGGING_LEVEL_MIP "INFO" }} -- GitLab From cedf93f60856d57a05a3e6c86cca3fd38f3921d8 Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Fri, 23 Oct 2020 12:41:03 +0300 Subject: [PATCH 04/52] Removed curl installation because build was failing. --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index a4fa1142f..458fbc8a3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,8 +14,7 @@ RUN cp /usr/share/maven/ref/settings-docker.xml /root/.m2/settings.xml \ FROM hbpmip/java-base:11.0.1-1 -RUN apt-get update && apt-get install -y --no-install-recommends curl \ - && rm -rf /var/lib/apt/lists/* /tmp/* +RUN rm -rf /var/lib/apt/lists/* /tmp/* COPY docker/config/application.tmpl /opt/portal/config/application.tmpl COPY docker/README.md docker/run.sh /opt/portal/ -- GitLab From 82acc556a0c063dda910c2f95ef3500ae9d199ca Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Fri, 23 Oct 2020 13:08:02 +0300 Subject: [PATCH 05/52] Reduced logs and removed spacing. --- docker/config/application.tmpl | 2 +- .../mip/configuration/SecurityConfiguration.java | 2 -- src/main/java/eu/hbp/mip/utils/Logging.java | 16 ---------------- 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index dce1ac742..8d76148db 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -50,7 +50,7 @@ frontend: logging: level: - root: {{ default .Env.LOG_LEVEL "INFO" }} + root: {{ default .Env.LOG_LEVEL "ERROR" }} org: springframework: security: ERROR diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index 7836edf94..4ea52570f 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -274,7 +274,6 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { public void logout() { // POSTã™ã‚‹ãƒªã‚¯ã‚¨ã‚¹ãƒˆãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã‚’ä½œæˆ - Logging.LogAction("refresh token ", this.oauth2ClientContext.getAccessToken().getRefreshToken().getValue()); RestTemplate restTemplate = new RestTemplate(); MultiValueMap<String, String> formParams = new LinkedMultiValueMap<>(); formParams.add("client_id", hbp().getClientId()); @@ -284,7 +283,6 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE); // ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’ä½œæˆ - Logging.LogAction("logoutUri is ", logoutUri); RequestEntity<MultiValueMap<String, String>> requestEntity = new RequestEntity<>(formParams, httpHeaders, HttpMethod.POST, URI.create(logoutUri)); diff --git a/src/main/java/eu/hbp/mip/utils/Logging.java b/src/main/java/eu/hbp/mip/utils/Logging.java index f3ed45e83..9b75de636 100644 --- a/src/main/java/eu/hbp/mip/utils/Logging.java +++ b/src/main/java/eu/hbp/mip/utils/Logging.java @@ -11,9 +11,7 @@ public class Logging { public static void LogUserAction(String userName, String endpoint, String actionInfo) { LOGGER.info(" User -> " + userName + " ," - + calculateAdditionalSpacing(userName.length(), 8) + "Endpoint -> " + endpoint + " ," - + calculateAdditionalSpacing(endpoint.length(), 32) + "Info -> " + actionInfo); } @@ -21,20 +19,6 @@ public class Logging { public static void LogExperimentAction(String experimentName, UUID experimentId, String actionInfo) { LOGGER.info(" Experiment -> " + experimentName + "(" + experimentId + ") ," - + calculateAdditionalSpacing(experimentName.length() + experimentId.toString().length() + 2, 20) + "Info -> " + actionInfo); } - - // Used when a user is not authorised yet - public static void LogAction(String actionName, String actionIdInfo) { - LOGGER.info(" Action -> " + actionName + " ," - + calculateAdditionalSpacing(actionName.length() + 2, 20) - + "Info -> " + actionIdInfo); - } - - // Calculates the spacing that is needed to create consistent logs. - private static String calculateAdditionalSpacing(Integer currentLen, Integer maxLen) { - int additionalSpacing = (maxLen > currentLen ? maxLen - currentLen + 2 : 2); - return String.format("%" + additionalSpacing + "s", ""); - } } -- GitLab From 168ff3318edc9aece5305b39f9ea47b4f708402a Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Fri, 23 Oct 2020 14:15:59 +0300 Subject: [PATCH 06/52] Log level default values and env variables changed. --- docker/README.md | 4 ++-- docker/config/application.tmpl | 10 ++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/docker/README.md b/docker/README.md index faedc351e..3f3437aec 100644 --- a/docker/README.md +++ b/docker/README.md @@ -46,8 +46,8 @@ To use this image, you need a running instance of PostgreSQL and to configure th ### LOGGING -* LOGGING_LEVEL_WEB: log level for the web layer of the application. Default to "WARN". -* LOGGING_LEVEL_HIBERNATE: log level for the Hibernate layer of the application. Default to "WARN". +* LOG_LEVEL: log level for the developer added logs. Default is "ERROR". +* LOG_LEVEL_FRAMEWORK: log level for all the framework logs. Default is "ERROR". ### ENDPOINTS diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index 8d76148db..13fc65779 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -50,15 +50,9 @@ frontend: logging: level: - root: {{ default .Env.LOG_LEVEL "ERROR" }} - org: - springframework: - security: ERROR - web: {{ default .Env.LOGGING_LEVEL_WEB "ERROR" }} - web.servlet.handler.BeanNameUrlHandlerMapping: ERROR - hibernate: {{ default .Env.LOGGING_LEVEL_HIBERNATE "ERROR" }} + root: {{ default .Env.LOG_LEVEL_FRAMEWORK "ERROR" }} eu: - hbp: {{ default .Env.LOGGING_LEVEL_MIP "INFO" }} + hbp: {{ default .Env.LOG_LEVEL "INFO" }} # EMBEDDED SERVER CONFIGURATION server: -- GitLab From f362422371acb8a524f6acca14e9521bb5871c47 Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Mon, 2 Nov 2020 04:40:37 -0800 Subject: [PATCH 07/52] Updated dependecies --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 87ef337aa..2caa3f349 100644 --- a/pom.xml +++ b/pom.xml @@ -209,7 +209,7 @@ <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> - <version>2.4.0-b180830.0438</version> + <version>3.0.0-M4</version> </dependency> <!-- https://mvnrepository.com/artifact/com.github.jmchilton.blend4j/blend4j --> <dependency> @@ -221,27 +221,27 @@ <dependency> <groupId>com.squareup.retrofit2</groupId> <artifactId>retrofit</artifactId> - <version>2.5.0</version> + <version>2.9.0</version> </dependency> <!-- https://mvnrepository.com/artifact/com.squareup.retrofit2/converter-gson --> <dependency> <groupId>com.squareup.retrofit2</groupId> <artifactId>converter-gson</artifactId> - <version>2.4.0</version> + <version>2.9.0</version> </dependency> <!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/logging-interceptor --> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>logging-interceptor</artifactId> - <version>3.12.1</version> + <version>4.10.0-RC1</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> - <version>3.8.3</version> + <version>3.11.0</version> </dependency> </dependencies> -- GitLab From e8cb6d0d48e53dcf5f264534be4918c9f22fe317 Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Thu, 5 Nov 2020 06:48:20 -0800 Subject: [PATCH 08/52] Updating pom.xml and implementing optional filtering in GET /experiments. --- docker/config/application.tmpl | 4 +- pom.xml | 65 ++++++++------- .../PersistenceConfiguration.java | 22 ++--- .../configuration/PortalErrorAttributes.java | 43 ---------- .../configuration/SecurityConfiguration.java | 2 +- .../mip/configuration/WebConfiguration.java | 10 +-- .../eu/hbp/mip/controllers/ExperimentApi.java | 13 +-- .../eu/hbp/mip/controllers/SecurityApi.java | 4 +- .../java/eu/hbp/mip/controllers/UsersApi.java | 8 +- .../{Article.java => DAOs/ArticleDAO.java} | 20 ++--- .../eu/hbp/mip/model/DAOs/ExperimentDAO.java | 42 +++++++--- .../model/{User.java => DAOs/UserDAO.java} | 8 +- .../eu/hbp/mip/model/DTOs/ExperimentDTO.java | 19 ++++- src/main/java/eu/hbp/mip/model/UserInfo.java | 11 +-- .../mip/repositories/ArticleRepository.java | 10 +-- .../repositories/ExperimentRepository.java | 9 +- .../hbp/mip/repositories/UserRepository.java | 5 +- .../hbp/mip/services/ExperimentService.java | 74 ++++++++--------- .../services/ExperimentSpecifications.java | 82 +++++++++++++++++++ .../V7_0__NewExperimentStructure.sql | 5 +- 20 files changed, 266 insertions(+), 190 deletions(-) delete mode 100644 src/main/java/eu/hbp/mip/configuration/PortalErrorAttributes.java rename src/main/java/eu/hbp/mip/model/{Article.java => DAOs/ArticleDAO.java} (87%) rename src/main/java/eu/hbp/mip/model/{User.java => DAOs/UserDAO.java} (94%) create mode 100644 src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index 13fc65779..4e723b8a3 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -2,7 +2,7 @@ # See http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html spring: - portalDatasource: + portal-datasource: url: {{ default .Env.PORTAL_DB_URL "jdbc:postgresql://172.22.0.1:5432/portal" }} schema: {{ default .Env.PORTAL_DB_SCHEMA "public" }} username: {{ default .Env.PORTAL_DB_USER "postgres" }} @@ -16,6 +16,8 @@ spring: jpa: hibernate: dialect: org.hibernate.dialect.PostgreSQL9Dialect + ddl-auto: validate + # SPRING RESOURCES HANDLING resources: chain: diff --git a/pom.xml b/pom.xml index 2caa3f349..02129ae2b 100644 --- a/pom.xml +++ b/pom.xml @@ -14,9 +14,7 @@ <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> - <version>1.3.8.RELEASE</version> - <relativePath/> - + <version>2.3.5.RELEASE</version> </parent> <properties> @@ -25,16 +23,18 @@ <sonar.projectName>MIP portal-backend</sonar.projectName> <sonar.sources>src/main/java/</sonar.sources> <java.version>1.8</java.version> + <springfox-swagger.version>2.5.0</springfox-swagger.version> + <spring-context.version>5.2.10.RELEASE</spring-context.version> + <springfox-boot-starter.version>3.0.0</springfox-boot-starter.version> <asciidoctor.maven.plugin.version>1.5.5</asciidoctor.maven.plugin.version> <asciidoctorj.pdf.version>1.5.0-alpha.15</asciidoctorj.pdf.version> <asciidoctorj.version>1.5.5</asciidoctorj.version> - <spring-data-commons.version>1.12.3.RELEASE</spring-data-commons.version> <angularjs.version>1.5.7</angularjs.version> <jquery.version>3.0.0</jquery.version> <bootstrap.version>3.3.7</bootstrap.version> + <webjars-locator.version>0.36</webjars-locator.version> <h2.version>1.4.192</h2.version> <postgresql.version>42.2.1</postgresql.version> - <springfox-swagger.version>2.5.0</springfox-swagger.version> <gson.version>2.7</gson.version> <slugify.version>2.1.5</slugify.version> <maven-resources-plugin.version>3.0.1</maven-resources-plugin.version> @@ -42,15 +42,12 @@ <swagger2markup-maven-plugin.version>1.0.0</swagger2markup-maven-plugin.version> <maven-compiler-plugin.version>3.5.1</maven-compiler-plugin.version> <hibernate4-maven-plugin.version>1.1.1</hibernate4-maven-plugin.version> + <commons-dbcp.version>2.7.0</commons-dbcp.version> <flyway-core.version>4.2.0</flyway-core.version> <hibernate-jpa-2.1-api.version>1.0.0.Final</hibernate-jpa-2.1-api.version> - <hibernate.version>4.3.11.Final</hibernate.version> - <spring-data-jpa.version>1.10.11.RELEASE</spring-data-jpa.version> - <spring-boot-starter-actuator.version>1.4.7.RELEASE</spring-boot-starter-actuator.version> + <hibernate.version>5.4.10.Final</hibernate.version> <aspectjweaver.version>1.8.9</aspectjweaver.version> <javax-inject.version>1</javax-inject.version> - <akka.version>2.5.22</akka.version> - <spring-context.version>4.3.4.RELEASE</spring-context.version> <protobuf-java.version>3.1.0</protobuf-java.version> </properties> @@ -66,32 +63,39 @@ </repositories> <dependencies> - + <dependency> + <groupId>net.logstash.logback</groupId> + <artifactId>logstash-logback-encoder</artifactId> + <version>6.4</version> + </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> + <dependency> + <groupId>org.springframework.security.oauth.boot</groupId> + <artifactId>spring-security-oauth2-autoconfigure</artifactId> + <version>2.0.1.RELEASE</version> + </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> - <version>${spring-boot-starter-actuator.version}</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-commons</artifactId> - <version>${spring-data-commons.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-web</artifactId> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-oauth2-resource-server</artifactId> </dependency> <dependency> - <groupId>org.springframework.security.oauth</groupId> - <artifactId>spring-security-oauth2</artifactId> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> @@ -115,6 +119,7 @@ <dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator</artifactId> + <version>${webjars-locator.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> @@ -148,13 +153,8 @@ </dependency> <dependency> <groupId>io.springfox</groupId> - <artifactId>springfox-swagger2</artifactId> - <version>${springfox-swagger.version}</version> - </dependency> - <dependency> - <groupId>io.springfox</groupId> - <artifactId>springfox-swagger-ui</artifactId> - <version>${springfox-swagger.version}</version> + <artifactId>springfox-boot-starter</artifactId> + <version>${springfox-boot-starter.version}</version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> @@ -169,11 +169,11 @@ <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> - <version>${spring-data-jpa.version}</version> </dependency> <dependency> - <groupId>commons-dbcp</groupId> - <artifactId>commons-dbcp</artifactId> + <groupId>org.apache.commons</groupId> + <artifactId>commons-dbcp2</artifactId> + <version>${commons-dbcp.version}</version> </dependency> <dependency> <groupId>org.flywaydb</groupId> @@ -192,9 +192,9 @@ <version>${javax-inject.version}</version> </dependency> <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-context</artifactId> - <version>${spring.version}</version> + <groupId>org.springframework</groupId> + <artifactId>spring-context</artifactId> + <version>${spring-context.version}</version> </dependency> <dependency> <groupId>com.google.protobuf</groupId> @@ -243,6 +243,11 @@ <artifactId>java-jwt</artifactId> <version>3.11.0</version> </dependency> + <dependency> + <groupId>org.springframework.security.oauth</groupId> + <artifactId>spring-security-oauth2</artifactId> + <version>2.5.0.RELEASE</version> + </dependency> </dependencies> <build> diff --git a/src/main/java/eu/hbp/mip/configuration/PersistenceConfiguration.java b/src/main/java/eu/hbp/mip/configuration/PersistenceConfiguration.java index c8024e1bf..bca25598a 100644 --- a/src/main/java/eu/hbp/mip/configuration/PersistenceConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/PersistenceConfiguration.java @@ -1,10 +1,12 @@ package eu.hbp.mip.configuration; import org.flywaydb.core.Flyway; -import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.orm.jpa.EntityScan; -import org.springframework.context.annotation.*; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; +import org.springframework.context.annotation.Primary; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; @@ -18,12 +20,11 @@ import javax.sql.DataSource; @Configuration @EnableJpaRepositories("eu.hbp.mip.repositories") -@EntityScan(basePackages = "eu.hbp.mip.model") public class PersistenceConfiguration { @Primary - @Bean(name = "portalDatasource") - @ConfigurationProperties(prefix = "spring.portalDatasource") + @Bean(name = "portal-datasource") + @ConfigurationProperties(prefix = "spring.portal-datasource") public DataSource portalDataSource() { return DataSourceBuilder.create().build(); } @@ -32,11 +33,12 @@ public class PersistenceConfiguration { @Bean(name = "entityManagerFactory") @DependsOn("flyway") public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); - em.setDataSource(portalDataSource()); + LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean(); + emfb.setDataSource(portalDataSource()); JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); - em.setJpaVendorAdapter(vendorAdapter); - return em; + emfb.setJpaVendorAdapter(vendorAdapter); + emfb.setPackagesToScan("eu.hbp.mip.model.DAOs"); + return emfb; } @Bean(name = "flyway", initMethod = "migrate") diff --git a/src/main/java/eu/hbp/mip/configuration/PortalErrorAttributes.java b/src/main/java/eu/hbp/mip/configuration/PortalErrorAttributes.java deleted file mode 100644 index 352b1d156..000000000 --- a/src/main/java/eu/hbp/mip/configuration/PortalErrorAttributes.java +++ /dev/null @@ -1,43 +0,0 @@ -package eu.hbp.mip.configuration; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.autoconfigure.web.DefaultErrorAttributes; -import org.springframework.stereotype.Component; -import org.springframework.web.context.request.RequestAttributes; - -import java.util.HashMap; -import java.util.Map; - -@Component -public class PortalErrorAttributes extends DefaultErrorAttributes { - - private static final Logger LOGGER = LoggerFactory.getLogger(PortalErrorAttributes.class); - - @Override - public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) { - Map<String, Object> errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace); - - Throwable throwable = getError(requestAttributes); - StringBuilder sb = new StringBuilder("["); - for (String attr : requestAttributes.getAttributeNames(RequestAttributes.SCOPE_REQUEST)) { - Object v = requestAttributes.getAttribute(attr, RequestAttributes.SCOPE_REQUEST); - sb.append(attr).append(" = ").append(v).append('\n'); - } - sb.append("]"); - LOGGER.error("Reporting server error on request with attributes " + sb.toString(), throwable); - - if (throwable != null) { - - Throwable cause = throwable.getCause(); - if (cause != null) { - Map<String, Object> causeErrorAttributes = new HashMap<>(); - causeErrorAttributes.put("exception", cause.getClass().getName()); - causeErrorAttributes.put("message", cause.getMessage()); - errorAttributes.put("cause", causeErrorAttributes); - } - } - - return errorAttributes; - } -} diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index 4ea52570f..afd3e56ed 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -9,7 +9,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.security.oauth2.resource.AuthoritiesExtractor; import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices; -import org.springframework.boot.context.embedded.FilterRegistrationBean; +import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java b/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java index a5b514dfb..17e38a5ee 100644 --- a/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java @@ -2,7 +2,6 @@ package eu.hbp.mip.configuration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; import org.springframework.data.web.config.EnableSpringDataWebSupport; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.RequestHandlerSelectors; @@ -10,7 +9,6 @@ import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger.web.UiConfiguration; import springfox.documentation.swagger2.annotations.EnableSwagger2; /** @@ -22,10 +20,10 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2; @EnableSpringDataWebSupport public class WebConfiguration { - @Bean - public UiConfiguration swaggerUiConfig() { - return UiConfiguration.DEFAULT; - } +// @Bean +// public String[] swaggerUiConfig() { +// return UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS; +// } @Bean public Docket swaggerDocumentation() { diff --git a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java index d28d8aac3..c3b93cbe1 100644 --- a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java +++ b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java @@ -1,6 +1,5 @@ package eu.hbp.mip.controllers; -import com.google.gson.Gson; import eu.hbp.mip.model.DTOs.ExperimentDTO; import eu.hbp.mip.model.UserInfo; import eu.hbp.mip.services.ExperimentService; @@ -8,13 +7,10 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; -import java.util.List; - import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; /* @@ -35,8 +31,13 @@ public class ExperimentApi { @ApiOperation(value = "Get experiments", response = ExperimentDTO.class, responseContainer = "List") @RequestMapping(method = RequestMethod.GET) - public ResponseEntity<String> getExperiments() { - return experimentService.getExperiments("(GET) /experiments"); + public ResponseEntity<String> getExperiments( + @RequestParam(name = "name", required = false) String name, + @RequestParam(name = "algorithm", required = false) String algorithm, + @RequestParam(name = "shared", required = false) Boolean shared, + @RequestParam(name = "viewed", required = false) Boolean viewed + ) { + return experimentService.getExperiments(name, algorithm, shared, viewed, "(GET) /experiments"); } @ApiOperation(value = "Get an experiment", response = ExperimentDTO.class) diff --git a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java index 4e412aea0..ff8d70a80 100644 --- a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java +++ b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.google.gson.JsonObject; import eu.hbp.mip.configuration.SecurityConfiguration; -import eu.hbp.mip.model.User; +import eu.hbp.mip.model.DAOs.UserDAO; import eu.hbp.mip.model.UserInfo; import eu.hbp.mip.repositories.UserRepository; import eu.hbp.mip.utils.Logging; @@ -70,7 +70,7 @@ public class SecurityApi { @RequestMapping(path = "/user", method = RequestMethod.POST) public ResponseEntity<Void> postUser( @ApiParam(value = "Has the user agreed on the NDA") @RequestParam(value = "agreeNDA") Boolean agreeNDA) { - User user = userInfo.getUser(); + UserDAO user = userInfo.getUser(); if (user != null) { user.setAgreeNDA(agreeNDA); userRepository.save(user); diff --git a/src/main/java/eu/hbp/mip/controllers/UsersApi.java b/src/main/java/eu/hbp/mip/controllers/UsersApi.java index b791a7858..886ad1ac9 100644 --- a/src/main/java/eu/hbp/mip/controllers/UsersApi.java +++ b/src/main/java/eu/hbp/mip/controllers/UsersApi.java @@ -4,7 +4,7 @@ package eu.hbp.mip.controllers; -import eu.hbp.mip.model.User; +import eu.hbp.mip.model.DAOs.UserDAO; import eu.hbp.mip.model.UserInfo; import eu.hbp.mip.repositories.UserRepository; import eu.hbp.mip.utils.Logging; @@ -31,13 +31,13 @@ public class UsersApi { @Autowired private UserInfo userInfo; - @ApiOperation(value = "Get a user", response = User.class) + @ApiOperation(value = "Get a user", response = UserDAO.class) @RequestMapping(value = "/{username}", method = RequestMethod.GET) - public ResponseEntity<User> getAUser( + public ResponseEntity<UserDAO> getAUser( @ApiParam(value = "username", required = true) @PathVariable("username") String username ) { Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /users/{username}", "Loaded a user with username : " + userInfo.getUser().getUsername()); - return ResponseEntity.ok(userRepository.findOne(username)); + return ResponseEntity.ok(userRepository.findByUsername(username)); } } diff --git a/src/main/java/eu/hbp/mip/model/Article.java b/src/main/java/eu/hbp/mip/model/DAOs/ArticleDAO.java similarity index 87% rename from src/main/java/eu/hbp/mip/model/Article.java rename to src/main/java/eu/hbp/mip/model/DAOs/ArticleDAO.java index b79f2a33a..541c50765 100644 --- a/src/main/java/eu/hbp/mip/model/Article.java +++ b/src/main/java/eu/hbp/mip/model/DAOs/ArticleDAO.java @@ -2,7 +2,7 @@ * Created by mirco on 04.12.15. */ -package eu.hbp.mip.model; +package eu.hbp.mip.model.DAOs; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; @@ -13,15 +13,13 @@ import javax.persistence.*; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import java.util.Date; -import java.util.LinkedList; -import java.util.List; @Entity @Table(name = "`article`") @ApiModel @JsonInclude(JsonInclude.Include.NON_NULL) @Validated -public class Article { +public class ArticleDAO { @Id private String slug = null; @@ -46,13 +44,13 @@ public class Article { @ManyToOne @JoinColumn(name = "createdby_username") - private User createdBy = null; + private UserDAO createdBy = null; @ManyToOne @JoinColumn(name = "updatedby_username") - private User updatedBy = null; + private UserDAO updatedBy = null; - public Article() { + public ArticleDAO() { /* * Empty constructor is needed by Hibernate */ @@ -133,20 +131,20 @@ public class Article { } - public User getCreatedBy() { + public UserDAO getCreatedBy() { return createdBy; } - public void setCreatedBy(User createdBy) { + public void setCreatedBy(UserDAO createdBy) { this.createdBy = createdBy; } - public User getUpdatedBy() { + public UserDAO getUpdatedBy() { return updatedBy; } - public void setUpdatedBy(User updatedBy) { + public void setUpdatedBy(UserDAO updatedBy) { this.updatedBy = updatedBy; } diff --git a/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java b/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java index 682f121ce..30d6c87da 100644 --- a/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java +++ b/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java @@ -1,11 +1,12 @@ package eu.hbp.mip.model.DAOs; +import com.fasterxml.jackson.annotation.JsonInclude; import com.google.gson.Gson; import com.google.gson.annotations.Expose; import eu.hbp.mip.model.DTOs.AlgorithmDTO; import eu.hbp.mip.model.DTOs.ExperimentDTO; -import eu.hbp.mip.model.User; import eu.hbp.mip.utils.JsonConverters; +import io.swagger.annotations.ApiModel; import javax.persistence.*; import java.util.Date; @@ -16,51 +17,62 @@ import java.util.UUID; */ @Entity @Table(name = "`experiment`") +@ApiModel +@JsonInclude(JsonInclude.Include.NON_NULL) public class ExperimentDAO { private static final Gson gson = new Gson(); + @Expose @Id @Column(columnDefinition = "uuid", updatable = false) @org.hibernate.annotations.Type(type = "pg-uuid") - @Expose private UUID uuid; - @Column(columnDefinition = "TEXT") @Expose + @Column(columnDefinition = "TEXT") private String name; @Expose @ManyToOne - @JoinColumn(name = "created_by_username") - private User createdBy; + @JoinColumn(name = "created_by_username",columnDefinition = "CHARACTER VARYING") + private UserDAO createdBy; - @Column(name="workflow_history_id", columnDefinition = "TEXT") @Expose + @Column(name="workflow_history_id", columnDefinition = "TEXT") private String workflowHistoryId; - @Column(columnDefinition = "TEXT") @Expose + @Column(columnDefinition = "TEXT") private Status status; - @Column(columnDefinition = "TEXT") @Expose + @Column(columnDefinition = "TEXT") private String result; @Expose + @Column(columnDefinition = "TIMESTAMP WITHOUT TIME ZONE") private Date finished; @Expose + @Column(columnDefinition = "TEXT") + private String algorithmDetails; + + @Expose + @Column(columnDefinition = "TEXT") private String algorithm; @Expose + @Column(columnDefinition = "TIMESTAMP WITHOUT TIME ZONE") private Date created = new Date(); @Expose + @Column(columnDefinition = "BOOLEAN") private boolean shared = false; // whether or not the experiment's result have been viewed by its owner @Expose + @Column(columnDefinition = "BOOLEAN") private boolean viewed = false; public enum Status { @@ -102,7 +114,7 @@ public class ExperimentDAO { public ExperimentDTO convertToDTO() { ExperimentDTO experimentDTO = new ExperimentDTO(); - experimentDTO.setAlgorithm(JsonConverters.convertJsonStringToObject(this.algorithm, AlgorithmDTO.class)); + experimentDTO.setAlgorithmDetails(JsonConverters.convertJsonStringToObject(this.algorithmDetails, AlgorithmDTO.class)); experimentDTO.setCreated(this.created); experimentDTO.setCreatedBy(this.createdBy.getUsername()); experimentDTO.setName(this.name); @@ -113,6 +125,14 @@ public class ExperimentDAO { return experimentDTO; } + public String getAlgorithmDetails() { + return algorithmDetails; + } + + public void setAlgorithmDetails(String algorithmDetails) { + this.algorithmDetails = algorithmDetails; + } + public String getAlgorithm() { return algorithm; } @@ -177,11 +197,11 @@ public class ExperimentDAO { this.name = name; } - public User getCreatedBy() { + public UserDAO getCreatedBy() { return createdBy; } - public void setCreatedBy(User createdBy) { + public void setCreatedBy(UserDAO createdBy) { this.createdBy = createdBy; } diff --git a/src/main/java/eu/hbp/mip/model/User.java b/src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java similarity index 94% rename from src/main/java/eu/hbp/mip/model/User.java rename to src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java index f1ee34059..1e35ac831 100644 --- a/src/main/java/eu/hbp/mip/model/User.java +++ b/src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java @@ -2,7 +2,7 @@ * Created by mirco on 04.12.15. */ -package eu.hbp.mip.model; +package eu.hbp.mip.model.DAOs; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; @@ -20,7 +20,7 @@ import java.util.regex.Pattern; @ApiModel @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(value = {"appsVotes"}) -public class User { +public class UserDAO { @Id @Expose @@ -35,7 +35,7 @@ public class User { private Boolean agreeNDA = null; - public User() { + public UserDAO() { /* * Empty constructor is needed by Hibernate */ @@ -47,7 +47,7 @@ public class User { * * @param userInfo info from OpenID UserInfo endpoint */ - public User(String userInfo) { + public UserDAO(String userInfo) { Pattern p; Matcher m; diff --git a/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java b/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java index b4214dd0a..e4b9d34a5 100644 --- a/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java +++ b/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java @@ -16,16 +16,29 @@ public class ExperimentDTO { private ExperimentDTO.ResultDTO result; private ExperimentDAO.Status status; - private AlgorithmDTO algorithm; + private String algorithm; + private AlgorithmDTO algorithmDetails; - public AlgorithmDTO getAlgorithm() { + public ExperimentDTO() { + } + + public String getAlgorithm() { return algorithm; } - public void setAlgorithm(AlgorithmDTO algorithm) { + public void setAlgorithm(String algorithm) { this.algorithm = algorithm; } + public AlgorithmDTO getAlgorithmDetails() { + return algorithmDetails; + } + + public void setAlgorithmDetails(AlgorithmDTO algorithmDetails) { + this.algorithmDetails = algorithmDetails; + this.algorithm = algorithmDetails.getName(); + } + public String getUuid() { return uuid; } diff --git a/src/main/java/eu/hbp/mip/model/UserInfo.java b/src/main/java/eu/hbp/mip/model/UserInfo.java index 7bd9a97f3..cb61cfcea 100644 --- a/src/main/java/eu/hbp/mip/model/UserInfo.java +++ b/src/main/java/eu/hbp/mip/model/UserInfo.java @@ -1,5 +1,6 @@ package eu.hbp.mip.model; +import eu.hbp.mip.model.DAOs.UserDAO; import eu.hbp.mip.repositories.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -26,7 +27,7 @@ public class UserInfo { @Value("#{'${hbp.authentication.enabled:1}'}") private boolean authentication; - private User user; + private UserDAO user; /** * Set to true if using no-auth mode and user has clicked on the login button @@ -45,18 +46,18 @@ public class UserInfo { * * @return the user for the current session */ - public User getUser() { + public UserDAO getUser() { if (user == null) { if (!authentication) { - user = new User(); + user = new UserDAO(); user.setUsername("anonymous"); user.setFullname("anonymous"); user.setEmail("anonymous@anonymous.com"); } else { - user = new User(getUserInfos()); + user = new UserDAO(getUserInfos()); } - User foundUser = userRepository.findOne(user.getUsername()); + UserDAO foundUser = userRepository.findByUsername(user.getUsername()); if (foundUser != null) { user.setAgreeNDA(foundUser.getAgreeNDA()); } diff --git a/src/main/java/eu/hbp/mip/repositories/ArticleRepository.java b/src/main/java/eu/hbp/mip/repositories/ArticleRepository.java index 1a93186ff..5f6589ab5 100644 --- a/src/main/java/eu/hbp/mip/repositories/ArticleRepository.java +++ b/src/main/java/eu/hbp/mip/repositories/ArticleRepository.java @@ -1,17 +1,17 @@ package eu.hbp.mip.repositories; -import eu.hbp.mip.model.Article; -import eu.hbp.mip.model.User; +import eu.hbp.mip.model.DAOs.ArticleDAO; +import eu.hbp.mip.model.DAOs.UserDAO; import org.springframework.data.repository.CrudRepository; /** * Created by mirco on 11.07.16. */ -public interface ArticleRepository extends CrudRepository<Article, String> { +public interface ArticleRepository extends CrudRepository<ArticleDAO, String> { Long countByTitle(String title); - Iterable<Article> findByCreatedBy(User user); + Iterable<ArticleDAO> findByCreatedBy(UserDAO user); - Iterable<Article> findByStatusOrCreatedBy(String status, User user); + Iterable<ArticleDAO> findByStatusOrCreatedBy(String status, UserDAO user); } diff --git a/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java b/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java index eb1d25415..a9fb10f27 100644 --- a/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java +++ b/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java @@ -1,7 +1,7 @@ package eu.hbp.mip.repositories; import eu.hbp.mip.model.DAOs.ExperimentDAO; -import eu.hbp.mip.model.User; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.CrudRepository; import java.util.UUID; @@ -10,8 +10,7 @@ import java.util.UUID; * Created by mirco on 11.07.16. */ -public interface ExperimentRepository extends CrudRepository<ExperimentDAO, UUID> { - Iterable<ExperimentDAO> findByCreatedBy(User user); - - Iterable<ExperimentDAO> findByShared(Boolean shared); +public interface ExperimentRepository extends CrudRepository<ExperimentDAO, UUID>, JpaSpecificationExecutor<ExperimentDAO> +{ + ExperimentDAO findByUuid(UUID experimentUuid); } diff --git a/src/main/java/eu/hbp/mip/repositories/UserRepository.java b/src/main/java/eu/hbp/mip/repositories/UserRepository.java index 3a0db7ee3..4a3a7c698 100644 --- a/src/main/java/eu/hbp/mip/repositories/UserRepository.java +++ b/src/main/java/eu/hbp/mip/repositories/UserRepository.java @@ -1,12 +1,13 @@ package eu.hbp.mip.repositories; -import eu.hbp.mip.model.User; +import eu.hbp.mip.model.DAOs.UserDAO; import org.springframework.data.repository.CrudRepository; /** * Created by mirco on 11.07.16. */ -public interface UserRepository extends CrudRepository<User, String> { +public interface UserRepository extends CrudRepository<UserDAO, String> { + UserDAO findByUsername(String username); } diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index bd0bed359..d8545b9be 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -6,14 +6,13 @@ import com.github.jmchilton.blend4j.galaxy.WorkflowsClient; import com.github.jmchilton.blend4j.galaxy.beans.Workflow; import com.github.jmchilton.blend4j.galaxy.beans.WorkflowDetails; import com.github.jmchilton.blend4j.galaxy.beans.WorkflowInputDefinition; -import com.google.common.collect.Lists; import com.google.gson.*; import eu.hbp.mip.controllers.galaxy.retrofit.RetroFitGalaxyClients; import eu.hbp.mip.controllers.galaxy.retrofit.RetrofitClientInstance; import eu.hbp.mip.model.DAOs.ExperimentDAO; +import eu.hbp.mip.model.DAOs.UserDAO; import eu.hbp.mip.model.DTOs.AlgorithmDTO; import eu.hbp.mip.model.DTOs.ExperimentDTO; -import eu.hbp.mip.model.User; import eu.hbp.mip.model.UserInfo; import eu.hbp.mip.model.galaxy.ErrorResponse; import eu.hbp.mip.model.galaxy.GalaxyWorkflowResult; @@ -27,6 +26,7 @@ import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.jpa.domain.Specification; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; @@ -51,12 +51,6 @@ public class ExperimentService { @Value("#{'${services.exareme.queryExaremeUrl}'}") private String queryExaremeUrl; - @Value("#{'${services.workflows.workflowUrl}'}") - private String workflowUrl; - - @Value("#{'${services.workflows.jwtSecret}'}") - private String jwtSecret; - @Value("#{'${services.galaxy.galaxyUrl}'}") private String galaxyUrl; @@ -75,7 +69,7 @@ public class ExperimentService { public ResponseEntity<String> getExperiment(String uuid, String endpoint) { ExperimentDAO experimentDAO; - User user = userInfo.getUser(); + UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Loading Experiment with uuid : " + uuid); @@ -97,7 +91,7 @@ public class ExperimentService { } public ResponseEntity<String> createExperiment(Authentication authentication, ExperimentDTO experimentDTO, String endpoint) { - User user = userInfo.getUser(); + UserDAO user = userInfo.getUser(); //Checking if check (POST) /experiments has proper input. if (checkPostExperimentProperInput(experimentDTO)){ @@ -106,11 +100,11 @@ public class ExperimentService { return ResponseEntity.badRequest().body("Please provide proper input."); } // Get the type and name of algorithm - String algorithmType = experimentDTO.getAlgorithm().getType(); - String algorithmName = experimentDTO.getAlgorithm().getName(); + String algorithmType = experimentDTO.getAlgorithmDetails().getType(); + String algorithmName = experimentDTO.getAlgorithmDetails().getName(); StringBuilder parametersLogMessage = new StringBuilder(", Parameters:\n"); - for (AlgorithmDTO.AlgorithmParamDTO params : experimentDTO.getAlgorithm().getParameters()) { + for (AlgorithmDTO.AlgorithmParamDTO params : experimentDTO.getAlgorithmDetails().getParameters()) { parametersLogMessage .append(" ") .append(params.getLabel()) @@ -123,7 +117,7 @@ public class ExperimentService { if (authenticationIsEnabled) { // Getting the dataset from the experiment parameters String experimentDatasets = null; - for (AlgorithmDTO.AlgorithmParamDTO parameter : experimentDTO.getAlgorithm().getParameters()) { + for (AlgorithmDTO.AlgorithmParamDTO parameter : experimentDTO.getAlgorithmDetails().getParameters()) { if (parameter.getLabel().equals("dataset")) { experimentDatasets = parameter.getValue(); break; @@ -152,19 +146,18 @@ public class ExperimentService { } } - public ResponseEntity<String> getExperiments( String endpoint) { - User user = userInfo.getUser(); + public ResponseEntity<String> getExperiments(String name,String algorithm, Boolean shared,Boolean viewed, String endpoint) { + UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Listing my experiments."); + Specification<ExperimentDAO> spec = Specification.where(new ExperimentSpecifications.ExperimentWithName(name)) + .and(new ExperimentSpecifications.ExperimentWithAlgorithm(algorithm)) + .and(new ExperimentSpecifications.ExperimentWithShared(shared)) + .and(new ExperimentSpecifications.ExperimentWithViewed(viewed)); - Iterable<ExperimentDAO> myExperiments = experimentRepository.findByCreatedBy(user); - List<ExperimentDAO> expList = Lists.newLinkedList(myExperiments); - Iterable<ExperimentDAO> sharedExperiments = experimentRepository.findByShared(true); - List<ExperimentDAO> sharedExpList = Lists.newLinkedList(sharedExperiments); - expList.addAll(sharedExpList); - Logging.LogUserAction(user.getUsername(), endpoint, "Successfully listed my experiments."); - List<ExperimentDTO> experimentDTOs = new LinkedList<>(); + List<ExperimentDAO> experimentDAOs = experimentRepository.findAll(spec); - for (ExperimentDAO experimentDAO: expList){ + List<ExperimentDTO> experimentDTOs= new ArrayList<>(); + for (ExperimentDAO experimentDAO: experimentDAOs){ ExperimentDTO experimentDTO = experimentDAO.convertToDTO(); experimentDTOs.add(experimentDTO); } @@ -174,7 +167,7 @@ public class ExperimentService { public ResponseEntity<String> updateExperiment(String uuid, ExperimentDTO experimentDTO, String endpoint) { ExperimentDAO experimentDAO; - User user = userInfo.getUser(); + UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Updating experiment with uuid : " + experimentDTO.getUuid() + "."); //Checking if check (PUT) /experiments has proper input. if (checkPutExperimentProperInput(experimentDTO)){ @@ -186,7 +179,7 @@ public class ExperimentService { if((experimentDTO.getName() == null || experimentDTO.getName().length() == 0) && experimentDTO.getShared() == null && experimentDTO.getViewed() == null - && experimentDTO.getAlgorithm() == null) + && experimentDTO.getAlgorithmDetails() == null) { return ResponseEntity.badRequest().body("Input is required."); } @@ -222,7 +215,7 @@ public class ExperimentService { public ResponseEntity<String> deleteExperiment(String uuid, String endpoint) { ExperimentDAO experimentDAO; - User user = userInfo.getUser(); + UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Deleting experiment with uuid : " + uuid + "."); experimentDAO = loadExperiment(uuid); @@ -266,30 +259,31 @@ public class ExperimentService { return null; } - return experimentRepository.findOne(experimentUuid); + return experimentRepository.findByUuid(experimentUuid); } private ExperimentDAO createExperiment(ExperimentDTO experimentDTO, String endpoint) { - User user = userInfo.getUser(); + UserDAO user = userInfo.getUser(); ExperimentDAO experimentDAO = new ExperimentDAO(); experimentDAO.setUuid(UUID.randomUUID()); experimentDAO.setCreatedBy(user); - experimentDAO.setAlgorithm(JsonConverters.convertObjectToJsonString(experimentDTO.getAlgorithm())); + experimentDAO.setAlgorithmDetails(JsonConverters.convertObjectToJsonString(experimentDTO.getAlgorithmDetails())); + experimentDAO.setAlgorithm(experimentDTO.getAlgorithm()); experimentDAO.setName(experimentDTO.getName()); experimentRepository.save(experimentDAO); Logging.LogUserAction(user.getUsername(), endpoint, " id : " + experimentDAO.getUuid()); - Logging.LogUserAction(user.getUsername(), endpoint, " algorithms : " + experimentDAO.getAlgorithm()); + Logging.LogUserAction(user.getUsername(), endpoint, " algorithms : " + experimentDAO.getAlgorithmDetails()); Logging.LogUserAction(user.getUsername(), endpoint, " name : " + experimentDAO.getName()); return experimentDAO; } private void saveExperiment(ExperimentDAO experimentDAO, String endpoint) { - User user = userInfo.getUser(); + UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, " id : " + experimentDAO.getUuid()); - Logging.LogUserAction(user.getUsername(), endpoint, " algorithms : " + experimentDAO.getAlgorithm()); + Logging.LogUserAction(user.getUsername(), endpoint, " algorithms : " + experimentDAO.getAlgorithmDetails()); Logging.LogUserAction(user.getUsername(), endpoint, " name : " + experimentDAO.getName()); Logging.LogUserAction(user.getUsername(), endpoint, " historyId : " + experimentDAO.getWorkflowHistoryId()); Logging.LogUserAction(user.getUsername(), endpoint, " status : " + experimentDAO.getStatus()); @@ -313,18 +307,18 @@ public class ExperimentService { * @return the response to be returned */ public ResponseEntity<String> runExaremeAlgorithm(ExperimentDTO experimentDTO, String endpoint) { - User user = userInfo.getUser(); + UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Running the algorithm..."); ExperimentDAO experimentDAO = createExperiment(experimentDTO, endpoint); Logging.LogUserAction(user.getUsername(), endpoint, "Created experiment with uuid :" + experimentDAO.getUuid()); // Run the 1st algorithm from the list - String algorithmName = experimentDTO.getAlgorithm().getName(); + String algorithmName = experimentDTO.getAlgorithmDetails().getName(); // Get the parameters List<AlgorithmDTO.AlgorithmParamDTO> algorithmParameters - = experimentDTO.getAlgorithm().getParameters(); + = experimentDTO.getAlgorithmDetails().getParameters(); String body = gson.toJson(algorithmParameters); String url = queryExaremeUrl + "/" + algorithmName; @@ -375,7 +369,7 @@ public class ExperimentService { * @return the response to be returned */ public ResponseEntity<String> runGalaxyWorkflow(ExperimentDTO experimentDTO, String endpoint) { - User user = userInfo.getUser(); + UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Running a workflow..."); ExperimentDAO experimentDAO = createExperiment(experimentDTO, endpoint); @@ -383,11 +377,11 @@ public class ExperimentService { // Run the 1st algorithm from the list - String workflowId = experimentDTO.getAlgorithm().getName(); + String workflowId = experimentDTO.getAlgorithmDetails().getName(); // Get the parameters List<AlgorithmDTO.AlgorithmParamDTO> algorithmParameters - = experimentDTO.getAlgorithm().getParameters(); + = experimentDTO.getAlgorithmDetails().getParameters(); // Convert the parameters to workflow parameters HashMap<String, String> algorithmParamsIncludingEmpty = new HashMap<>(); @@ -481,7 +475,7 @@ public class ExperimentService { * @return nothing, just updates the experiment */ public void updateWorkflowExperiment(ExperimentDAO experimentDAO, String endpoint) { - User user = userInfo.getUser(); + UserDAO user = userInfo.getUser(); if (experimentDAO == null) { Logging.LogUserAction(user.getUsername(), endpoint, "The experiment does not exist."); diff --git a/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java b/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java new file mode 100644 index 000000000..6596a319a --- /dev/null +++ b/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java @@ -0,0 +1,82 @@ +package eu.hbp.mip.services; + +import eu.hbp.mip.model.DAOs.ExperimentDAO; +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; + +public class ExperimentSpecifications { + public static class ExperimentWithName implements Specification<ExperimentDAO> { + + private String name; + private String regExp; + + public ExperimentWithName(String name){ + this.name = name; + this.regExp = "%"+name+"%"; + } + + public Predicate toPredicate(Root<ExperimentDAO> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) + { + if (name == null) { + return cb.isTrue(cb.literal(true)); + } + + return cb.like( root.get( "name" ), regExp ); + } + } + public static class ExperimentWithAlgorithm implements Specification<ExperimentDAO> { + + private String algorithm; + + public ExperimentWithAlgorithm(String algorithm){ + this.algorithm = algorithm; + } + + public Predicate toPredicate(Root<ExperimentDAO> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) + { + if (algorithm == null) { + return cb.isTrue(cb.literal(true)); + } + + return cb.equal(root.get("algorithm"), this.algorithm); + } + } + + public static class ExperimentWithViewed implements Specification<ExperimentDAO> { + + private Boolean viewed; + + public ExperimentWithViewed(Boolean viewed){ + this.viewed = viewed; + } + + public Predicate toPredicate(Root<ExperimentDAO> root, CriteriaQuery<?> query, CriteriaBuilder cb) { + if (viewed == null) { + return cb.isTrue(cb.literal(true)); // always true = no filtering + } + return cb.equal(root.get("viewed"), this.viewed); + } + } + + public static class ExperimentWithShared implements org.springframework.data.jpa.domain.Specification<ExperimentDAO> { + + private Boolean shared; + + public ExperimentWithShared(Boolean shared){ + this.shared = shared; + } + + public Predicate toPredicate(Root<ExperimentDAO> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) { + if (shared == null) { + return cb.isTrue(cb.literal(true)); + } + return cb.equal(root.get("shared"), this.shared); + } + + } +} + diff --git a/src/main/resources/db/migration/V7_0__NewExperimentStructure.sql b/src/main/resources/db/migration/V7_0__NewExperimentStructure.sql index b69ae2b70..73a15addd 100644 --- a/src/main/resources/db/migration/V7_0__NewExperimentStructure.sql +++ b/src/main/resources/db/migration/V7_0__NewExperimentStructure.sql @@ -5,7 +5,7 @@ DROP COLUMN validations, DROP COLUMN model_slug; ALTER TABLE experiment -RENAME algorithms TO algorithm; +RENAME algorithms TO algorithmDetails; ALTER TABLE experiment RENAME createdby_username TO created_by_username; ALTER TABLE experiment @@ -15,6 +15,9 @@ RENAME resultsviewed TO viewed; ALTER TABLE experiment RENAME workflowstatus TO status; +ALTER TABLE experiment +ADD COLUMN algorithm text; + ALTER TABLE "user" DROP COLUMN birthday, DROP COLUMN city, -- GitLab From e2ad3a7a25f5af81649cfb5a506233e5899cf639 Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Fri, 6 Nov 2020 01:29:22 -0800 Subject: [PATCH 09/52] Improved exception handling. --- .../eu/hbp/mip/controllers/AlgorithmsApi.java | 3 +- .../eu/hbp/mip/controllers/ExperimentApi.java | 6 +- .../hbp/mip/controllers/PathologiesApi.java | 3 +- .../repositories/ExperimentRepository.java | 3 +- .../hbp/mip/services/ExperimentService.java | 83 +++++++++++-------- .../mip/utils/ControllerExceptionHandler.java | 35 ++++++++ .../java/eu/hbp/mip/utils/ErrorMessage.java | 34 ++++++++ .../java/eu/hbp/mip/utils/ErrorMessages.java | 7 -- .../mip/utils/ResourceNotFoundException.java | 10 +++ 9 files changed, 136 insertions(+), 48 deletions(-) create mode 100644 src/main/java/eu/hbp/mip/utils/ControllerExceptionHandler.java create mode 100644 src/main/java/eu/hbp/mip/utils/ErrorMessage.java delete mode 100644 src/main/java/eu/hbp/mip/utils/ErrorMessages.java create mode 100644 src/main/java/eu/hbp/mip/utils/ResourceNotFoundException.java diff --git a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java index fcde558b8..d5dca19ae 100644 --- a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java +++ b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java @@ -31,7 +31,6 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; -import static eu.hbp.mip.utils.ErrorMessages.disabledAlgorithmsCouldNotBeLoaded; import static eu.hbp.mip.utils.InputStreamConverter.convertInputStreamToString; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @@ -83,7 +82,7 @@ public class AlgorithmsApi { disabledAlgorithms = getDisabledAlgorithms(); } catch (IOException e) { Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", - disabledAlgorithmsCouldNotBeLoaded); + "The disabled algorithms could not be loaded."); } // Remove any disabled algorithm diff --git a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java index c3b93cbe1..7cb949a4b 100644 --- a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java +++ b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java @@ -35,9 +35,11 @@ public class ExperimentApi { @RequestParam(name = "name", required = false) String name, @RequestParam(name = "algorithm", required = false) String algorithm, @RequestParam(name = "shared", required = false) Boolean shared, - @RequestParam(name = "viewed", required = false) Boolean viewed + @RequestParam(name = "viewed", required = false) Boolean viewed, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "3") int size ) { - return experimentService.getExperiments(name, algorithm, shared, viewed, "(GET) /experiments"); + return experimentService.getExperiments(name, algorithm, shared, viewed, page, size, "(GET) /experiments"); } @ApiOperation(value = "Get an experiment", response = ExperimentDTO.class) diff --git a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java index 66a97896b..9560cc6dd 100644 --- a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java +++ b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java @@ -25,7 +25,6 @@ import org.springframework.web.bind.annotation.RestController; import java.io.IOException; import java.util.List; -import static eu.hbp.mip.utils.ErrorMessages.pathologiesCouldNotBeLoaded; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @RestController @@ -57,7 +56,7 @@ public class PathologiesApi { }.getType()); } catch (IOException e) { Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /pathologies", "Unable to load pathologies"); - return ResponseEntity.badRequest().body(pathologiesCouldNotBeLoaded); + return ResponseEntity.badRequest().body("The pathologies could not be loaded."); } // If authentication is disabled return everything diff --git a/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java b/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java index a9fb10f27..5f2c8dc76 100644 --- a/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java +++ b/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java @@ -4,6 +4,7 @@ import eu.hbp.mip.model.DAOs.ExperimentDAO; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.CrudRepository; +import java.util.Optional; import java.util.UUID; /** @@ -12,5 +13,5 @@ import java.util.UUID; public interface ExperimentRepository extends CrudRepository<ExperimentDAO, UUID>, JpaSpecificationExecutor<ExperimentDAO> { - ExperimentDAO findByUuid(UUID experimentUuid); + Optional<ExperimentDAO> findByUuid(UUID experimentUuid); } diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index d8545b9be..a02af3ab6 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -18,14 +18,14 @@ import eu.hbp.mip.model.galaxy.ErrorResponse; import eu.hbp.mip.model.galaxy.GalaxyWorkflowResult; import eu.hbp.mip.model.galaxy.PostWorkflowToGalaxyDtoResponse; import eu.hbp.mip.repositories.ExperimentRepository; -import eu.hbp.mip.utils.ClaimUtils; -import eu.hbp.mip.utils.HTTPUtil; -import eu.hbp.mip.utils.JsonConverters; -import eu.hbp.mip.utils.Logging; +import eu.hbp.mip.utils.*; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -45,9 +45,6 @@ public class ExperimentService { @Autowired private UserInfo userInfo; - private static final Gson gsonOnlyExposed = new GsonBuilder().serializeNulls() - .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").excludeFieldsWithoutExposeAnnotation().create(); - @Value("#{'${services.exareme.queryExaremeUrl}'}") private String queryExaremeUrl; @@ -66,6 +63,36 @@ public class ExperimentService { private static final Gson gson = new Gson(); + public ResponseEntity<String> getExperiments(String name,String algorithm, Boolean shared,Boolean viewed, int page, int size, String endpoint) { + UserDAO user = userInfo.getUser(); + Logging.LogUserAction(user.getUsername(), endpoint, "Listing my experiments."); + if(size > 10 ) + return ResponseEntity.badRequest().body("Invalid size input, max size is 10."); + + Specification<ExperimentDAO> spec = Specification.where(new ExperimentSpecifications.ExperimentWithName(name)) + .and(new ExperimentSpecifications.ExperimentWithAlgorithm(algorithm)) + .and(new ExperimentSpecifications.ExperimentWithShared(shared)) + .and(new ExperimentSpecifications.ExperimentWithViewed(viewed)); + + Pageable paging = PageRequest.of(page, size); + Page<ExperimentDAO> pageExperiments = experimentRepository.findAll(spec, paging); + List<ExperimentDAO> experimentDAOs = pageExperiments.getContent(); + + if (experimentDAOs.isEmpty()) + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + + List<ExperimentDTO> experimentDTOs = new ArrayList<>(); + experimentDAOs.forEach(experimentDAO -> experimentDTOs.add(experimentDAO.convertToDTO())); + + Map<String, Object> response = new HashMap<>(); + response.put("experiments", experimentDTOs); + response.put("currentPage", pageExperiments.getNumber()); + response.put("totalExperiments", pageExperiments.getTotalElements()); + response.put("totalPages", pageExperiments.getTotalPages()); + + return new ResponseEntity(response, HttpStatus.OK); + } + public ResponseEntity<String> getExperiment(String uuid, String endpoint) { ExperimentDAO experimentDAO; @@ -73,7 +100,7 @@ public class ExperimentService { Logging.LogUserAction(user.getUsername(), endpoint, "Loading Experiment with uuid : " + uuid); - experimentDAO = loadExperiment(uuid); + experimentDAO = loadExperiment(uuid).orElseThrow(() -> new ResourceNotFoundException("Not found Experimnet with id = " + uuid)); if (experimentDAO == null) { Logging.LogUserAction(user.getUsername(), endpoint, "Experiment Not found."); @@ -142,26 +169,8 @@ public class ExperimentService { return runGalaxyWorkflow(experimentDTO, endpoint); } else { Logging.LogUserAction(user.getUsername(), endpoint, "Algorithm runs on Exareme."); - return runExaremeAlgorithm(experimentDTO, endpoint); - } - } - - public ResponseEntity<String> getExperiments(String name,String algorithm, Boolean shared,Boolean viewed, String endpoint) { - UserDAO user = userInfo.getUser(); - Logging.LogUserAction(user.getUsername(), endpoint, "Listing my experiments."); - Specification<ExperimentDAO> spec = Specification.where(new ExperimentSpecifications.ExperimentWithName(name)) - .and(new ExperimentSpecifications.ExperimentWithAlgorithm(algorithm)) - .and(new ExperimentSpecifications.ExperimentWithShared(shared)) - .and(new ExperimentSpecifications.ExperimentWithViewed(viewed)); - - List<ExperimentDAO> experimentDAOs = experimentRepository.findAll(spec); - - List<ExperimentDTO> experimentDTOs= new ArrayList<>(); - for (ExperimentDAO experimentDAO: experimentDAOs){ - ExperimentDTO experimentDTO = experimentDAO.convertToDTO(); - experimentDTOs.add(experimentDTO); + return createExaremeExperiment(experimentDTO, endpoint); } - return new ResponseEntity<>(new Gson().toJson(experimentDTOs ), HttpStatus.OK); } public ResponseEntity<String> updateExperiment(String uuid, ExperimentDTO experimentDTO, String endpoint) @@ -184,7 +193,7 @@ public class ExperimentService { return ResponseEntity.badRequest().body("Input is required."); } - experimentDAO = loadExperiment(uuid); + experimentDAO = loadExperiment(uuid).get(); if (!experimentDAO.getCreatedBy().getUsername().equals(user.getUsername())) return new ResponseEntity<>("You're not the owner of this experiment", HttpStatus.UNAUTHORIZED); @@ -218,7 +227,7 @@ public class ExperimentService { UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Deleting experiment with uuid : " + uuid + "."); - experimentDAO = loadExperiment(uuid); + experimentDAO = loadExperiment(uuid).get(); if (!experimentDAO.getCreatedBy().getUsername().equals(user.getUsername())) return new ResponseEntity<>(endpoint, HttpStatus.UNAUTHORIZED); @@ -226,7 +235,7 @@ public class ExperimentService { experimentRepository.delete(experimentDAO); Logging.LogUserAction(user.getUsername(), endpoint, "Deleted experiment with uuid : " + uuid + "."); - return new ResponseEntity<>(HttpStatus.OK); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } // /* ------------------------------- PRIVATE METHODS ----------------------------------------------------*/ @@ -249,7 +258,8 @@ public class ExperimentService { || experimentDTO.getStatus() != null; } - private ExperimentDAO loadExperiment(String uuid){ + private Optional<ExperimentDAO> loadExperiment(String uuid){ + UUID experimentUuid ; @@ -306,7 +316,7 @@ public class ExperimentService { * @param experimentDTO is the request with the experiment information * @return the response to be returned */ - public ResponseEntity<String> runExaremeAlgorithm(ExperimentDTO experimentDTO, String endpoint) { + public ResponseEntity<String> createExaremeExperiment(ExperimentDTO experimentDTO, String endpoint) { UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Running the algorithm..."); @@ -330,10 +340,13 @@ public class ExperimentService { Logging.LogUserAction(user.getUsername(), endpoint, "Starting exareme execution thread"); new Thread(() -> { + // ATTENTION: Inside the Thread only LogExperimentAction should be used, not LogUserAction! Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Thread named :" + Thread.currentThread().getName() + " with id :" + Thread.currentThread().getId() + " started!"); try { + + // --> New function runExaremeAlgorithm StringBuilder results = new StringBuilder(); int code = HTTPUtil.sendPost(url, body, results); @@ -342,6 +355,8 @@ public class ExperimentService { // Results are stored in the experiment object ExperimentDTO.ResultDTO resultDTO = JsonConverters.convertJsonStringToObject(String.valueOf(results), ExperimentDTO.ResultDTO.class); + // <-- New function runExaremeAlgorithm + experimentDAO.setResult(JsonConverters.convertObjectToJsonString(resultDTO)); // experimentDAO.setHasError(code >= 400); // experimentDAO.setHasServerError(code >= 500); @@ -356,7 +371,7 @@ public class ExperimentService { Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Finished the experiment: " + experimentDAO.toString()); }).start(); experimentDTO = experimentDAO.convertToDTO(); - return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); + return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.CREATED); } /* --------------------------------------- GALAXY CALLS ---------------------------------------------------------*/ @@ -463,7 +478,7 @@ public class ExperimentService { Logging.LogUserAction(user.getUsername(), endpoint, "Run workflow completed!"); experimentDTO = experimentDAO.convertToDTO(); - return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); + return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.CREATED); } diff --git a/src/main/java/eu/hbp/mip/utils/ControllerExceptionHandler.java b/src/main/java/eu/hbp/mip/utils/ControllerExceptionHandler.java new file mode 100644 index 000000000..4e6f8da32 --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/ControllerExceptionHandler.java @@ -0,0 +1,35 @@ +package eu.hbp.mip.utils; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.context.request.WebRequest; + +import java.util.Date; + +@ControllerAdvice +public class ControllerExceptionHandler { + + @ExceptionHandler(ResourceNotFoundException.class) + public ResponseEntity<ErrorMessage> resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) { + ErrorMessage message = new ErrorMessage( + 2, + new Date(), + ex.getMessage(), + request.getDescription(false)); + + return new ResponseEntity<>(message, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity<ErrorMessage> globalExceptionHandler(Exception ex, WebRequest request) { + ErrorMessage message = new ErrorMessage( + HttpStatus.INTERNAL_SERVER_ERROR.value(), + new Date(), + ex.getMessage(), + request.getDescription(false)); + + return new ResponseEntity<>(message, HttpStatus.INTERNAL_SERVER_ERROR); + } +} diff --git a/src/main/java/eu/hbp/mip/utils/ErrorMessage.java b/src/main/java/eu/hbp/mip/utils/ErrorMessage.java new file mode 100644 index 000000000..ef15c71ae --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/ErrorMessage.java @@ -0,0 +1,34 @@ +package eu.hbp.mip.utils; + + +import java.util.Date; + +public class ErrorMessage { + private int statusCode; + private Date timestamp; + private String message; + private String description; + + public ErrorMessage(int statusCode, Date timestamp, String message, String description) { + this.statusCode = statusCode; + this.timestamp = timestamp; + this.message = message; + this.description = description; + } + + public int getStatusCode() { + return statusCode; + } + + public Date getTimestamp() { + return timestamp; + } + + public String getMessage() { + return message; + } + + public String getDescription() { + return description; + } +} diff --git a/src/main/java/eu/hbp/mip/utils/ErrorMessages.java b/src/main/java/eu/hbp/mip/utils/ErrorMessages.java deleted file mode 100644 index 9f672f81b..000000000 --- a/src/main/java/eu/hbp/mip/utils/ErrorMessages.java +++ /dev/null @@ -1,7 +0,0 @@ -package eu.hbp.mip.utils; - -public class ErrorMessages { - - public static String pathologiesCouldNotBeLoaded = "The pathologies could not be loaded."; - public static String disabledAlgorithmsCouldNotBeLoaded = "The disabled algorithms could not be loaded."; -} diff --git a/src/main/java/eu/hbp/mip/utils/ResourceNotFoundException.java b/src/main/java/eu/hbp/mip/utils/ResourceNotFoundException.java new file mode 100644 index 000000000..38f067c42 --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/ResourceNotFoundException.java @@ -0,0 +1,10 @@ +package eu.hbp.mip.utils; + +public class ResourceNotFoundException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public ResourceNotFoundException(String msg) { + super(msg); + } +} \ No newline at end of file -- GitLab From 78583a675de474db03ff8eda5e013f8045f83fed Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Fri, 6 Nov 2020 12:53:30 +0200 Subject: [PATCH 10/52] Cleaning up security configuration code. --- Dockerfile | 7 +- docker/README.md | 4 +- docker/config/application.tmpl | 16 +-- .../configuration/SecurityConfiguration.java | 134 ++++++------------ .../eu/hbp/mip/controllers/SecurityApi.java | 5 +- 5 files changed, 53 insertions(+), 113 deletions(-) diff --git a/Dockerfile b/Dockerfile index 458fbc8a3..866c01bf4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,17 +43,16 @@ WORKDIR /home/portal ENTRYPOINT ["/run.sh"] # 8080: Web service API, health checks on http://host:8080$CONTEXT_PATH/health -# 4089: Akka cluster -EXPOSE 4089 8080 +EXPOSE 8080 HEALTHCHECK --start-period=60s CMD curl -v --silent http://localhost:8080$CONTEXT_PATH/health 2>&1 | grep UP LABEL org.label-schema.build-date=$BUILD_DATE \ org.label-schema.name="hbpmip/portal-backend" \ - org.label-schema.description="Java backend for the MIP portal" \ + org.label-schema.description="Spring backend for the MIP portal" \ org.label-schema.url="https://mip.humanbrainproject.eu" \ org.label-schema.vcs-type="git" \ - org.label-schema.vcs-url="https://github.com/LREN-CHUV/portal-backend" \ + org.label-schema.vcs-url="https://github.com/HBPMedical/portal-backend" \ org.label-schema.vcs-ref=$VCS_REF \ org.label-schema.version="$VERSION" \ org.label-schema.vendor="LREN CHUV" \ diff --git a/docker/README.md b/docker/README.md index 3f3437aec..cc8c59fe1 100644 --- a/docker/README.md +++ b/docker/README.md @@ -72,4 +72,6 @@ To use this image, you need a running instance of PostgreSQL and to configure th * RELEASE_STAGE: Release stage used when reporting errors to Bugsnag. Values are dev, staging, production * DATA_CENTER_LOCATION: Location of the datacenter, used when reporting errors to Bugsnag -* CONTAINER_ORCHESTRATION: Container orchestration system used to execute the Docker containers. Values are mesos, docker-compose, kubernetes + + +# TODO Refactor variables \ No newline at end of file diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index 4e723b8a3..190f04f90 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -30,26 +30,14 @@ hbp: client: clientId: {{ default .Env.CLIENT_ID "996f97c5-a3ca-460e-b18b-00df3e2be89a" }} clientSecret: {{ .Env.CLIENT_SECRET }} - accessTokenUri: {{ default .Env.TOKEN_URI "https://services.humanbrainproject.eu/oidc/token" }} - userAuthorizationUri: {{ default .Env.AUTH_URI "https://services.humanbrainproject.eu/oidc/authorize" }} logoutUri: {{ .Env.LOGOUT_URI }} - tokenName: access_token - authenticationScheme: query - clientAuthenticationScheme: form - useCurrentUri: false - preEstablishedRedirectUri: {{ default .Env.FRONTEND_LOGIN_URL "http://frontend/services/login/hbp" }} - resource: - userInfoUri: {{ default .Env.USER_INFO_URI "https://services.humanbrainproject.eu/oidc/userinfo" }} - revokeTokenUri: {{ default .Env.REVOKE_TOKEN_URI "https://services.humanbrainproject.eu/oidc/slo" }} - sso: - login-path: # WEB FRONTEND frontend: loginUrl: {{ default .Env.FRONTEND_LOGIN_URL "http://frontend/services/login/hbp" }} + redirectAfterLoginUrl: {{ default .Env.FRONTEND_AFTER_LOGIN_URL "http://frontend/" }} redirectAfterLogoutUrl: {{ default .Env.FRONTEND_AFTER_LOGOUT_URL (default .Env.LOGIN_URI "http://frontend/services/login/hbp") }} - redirectAfterLoginUrl: {{ default .Env.FRONTEND_AFTER_LOGIN_URL "http://frontend/home" }} - + logging: level: root: {{ default .Env.LOG_LEVEL_FRAMEWORK "ERROR" }} diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index afd3e56ed..ff28602a1 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -1,7 +1,8 @@ package eu.hbp.mip.configuration; -import eu.hbp.mip.model.UserInfo; -import eu.hbp.mip.utils.*; +import eu.hbp.mip.utils.CORSFilter; +import eu.hbp.mip.utils.CustomAccessDeniedHandler; +import eu.hbp.mip.utils.CustomLoginUrlAuthenticationEntryPoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -9,14 +10,16 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.security.oauth2.resource.AuthoritiesExtractor; import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices; -import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.*; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.RequestEntity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.oauth2.client.OAuth2ClientContext; @@ -57,8 +60,9 @@ import java.util.List; import java.util.Map; -// See https://spring.io/guides/tutorials/spring-boot-oauth2/ for reference about configuring OAuth2 login +// Reference for OAuth2 login: https://spring.io/guides/tutorials/spring-boot-oauth2/ // also http://cscarioni.blogspot.ch/2013/04/pro-spring-security-and-oauth-2.html +// Security with Keycloak: https://www.thomasvitale.com/keycloak-authentication-flow-sso-client/ @Configuration @EnableOAuth2Client @@ -72,38 +76,40 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { /** * Enable HBP collab authentication (1) or disable it (0). Default is 1 */ - @Value("#{'${hbp.authentication.enabled:1}'}") - private boolean authentication; + @Value("#{'${hbp.authentication.enabled}'}") + private boolean authenticationEnabled; /** * Absolute URL to redirect to when login is required */ - @Value("#{'${frontend.loginUrl:/login/hbp}'}") + @Value("#{'${frontend.loginUrl}'}") private String loginUrl; /** * Absolute URL to redirect to when logout is required */ @Value("#{'${hbp.client.logoutUri}'}") - private String logoutUri; + private String logoutUrl; /** * Absolute URL to redirect to after successful login */ - @Value("#{'${frontend.redirectAfterLoginUrl:http://frontend/home}'}") + @Value("#{'${frontend.redirectAfterLoginUrl}'}") private String frontendRedirectAfterLogin; /** - * Absolute URL to redirect to after logout has occurred + * Absolute URL to redirect to after successful logout */ - @Value("#{'${frontend.redirectAfterLogoutUrl:/login/hbp}'}") + @Value("#{'${frontend.redirectAfterLogoutUrl}'}") private String redirectAfterLogoutUrl; - /** - * URL to revoke auth token - */ - @Value("#{'${hbp.resource.revokeTokenUri:https://services.humanbrainproject.eu/oidc/revoke}'}") - private String revokeTokenURI; + public boolean getAuthenticationEnabled() { + return authenticationEnabled; + } + + public String getFrontendRedirectAfterLogin() { + return frontendRedirectAfterLogin; + } @Override protected void configure(HttpSecurity http) throws Exception { @@ -111,15 +117,15 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { // @formatter:off http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class); - if (authentication) { + if (authenticationEnabled) { http.antMatcher("/**") .authorizeRequests() .antMatchers( - "/", "/login/**", "/health/**", "/info/**", "/metrics/**", "/trace/**", "/frontend/**", "/webjars/**", "/v2/api-docs", "/swagger-ui.html", "/swagger-resources/**" - ) - .permitAll() + "/", "/login/**", "/health/**", "/info/**", "/metrics/**", + "/trace/**", "/frontend/**", "/webjars/**", "/v2/api-docs", + "/swagger-ui.html", "/swagger-resources/**" + ).permitAll() .antMatchers("/galaxy*", "/galaxy/*").hasRole("Data Manager") - //.anyRequest().authenticated() .anyRequest().hasRole("Researcher") .and().exceptionHandling().authenticationEntryPoint(new CustomLoginUrlAuthenticationEntryPoint(loginUrl)) .accessDeniedHandler(new CustomAccessDeniedHandler()) @@ -131,7 +137,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { } else { http.antMatcher("/**") .authorizeRequests() - .antMatchers("/**").permitAll().and().csrf().disable(); + .antMatchers("/**").permitAll() + .and().csrf().disable(); } } @@ -165,13 +172,6 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { return new ResourceServerProperties(); } - public boolean isAuthentication() { - return authentication; - } - - public String getFrontendRedirectAfterLogin() { - return frontendRedirectAfterLogin; - } private Filter csrfHeaderFilter() { return new OncePerRequestFilter() { @@ -199,43 +199,6 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { return repository; } - private class CustomLogoutHandler implements LogoutHandler { - @Override - public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) { - - // Hackish way of accessing to this information... - final UserInfo userInfo = (UserInfo) httpServletRequest.getSession().getAttribute("userInfo"); - if (userInfo != null) { - userInfo.setFakeAuth(false); - } - - if (oauth2ClientContext == null || oauth2ClientContext.getAccessToken() == null) { - return; - } - - String idToken = oauth2ClientContext.getAccessToken().getAdditionalInformation().get("id_token").toString(); - - StringBuilder query = new StringBuilder(); - query.append("{"); - query.append("\"token\":"); - query.append("\"").append(idToken).append("\""); - query.append("}"); - - try { - int responseCode = HTTPUtil.sendPost(revokeTokenURI, query.toString(), new StringBuilder()); - if (responseCode != 200) { - LOGGER.warn("Cannot send request to OIDC server for revocation ! "); - } else { - LOGGER.info("Should be logged out"); - } - } catch (IOException e) { - LOGGER.warn("Cannot notify logout to OIDC server !"); - LOGGER.trace("Cannot notify logout", e); - } - - } - } - @Bean public AuthoritiesExtractor keycloakAuthoritiesExtractor() { return new KeycloakAuthoritiesExtractor(); @@ -273,33 +236,31 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { public void logout() { - // POSTã™ã‚‹ãƒªã‚¯ã‚¨ã‚¹ãƒˆãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã‚’ä½œæˆ + // TODO Try removing + RestTemplate restTemplate = new RestTemplate(); MultiValueMap<String, String> formParams = new LinkedMultiValueMap<>(); formParams.add("client_id", hbp().getClientId()); formParams.add("client_secret", hbp().getClientSecret()); formParams.add("refresh_token", this.oauth2ClientContext.getAccessToken().getRefreshToken().getValue()); - // ãƒªã‚¯ã‚¨ã‚¹ãƒˆãƒ˜ãƒƒãƒ€ãƒ¼ã‚’ä½œæˆ + HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE); - // ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’ä½œæˆ RequestEntity<MultiValueMap<String, String>> requestEntity = new RequestEntity<>(formParams, httpHeaders, HttpMethod.POST, - URI.create(logoutUri)); - // POSTリクエストé€ä¿¡ï¼ˆãƒã‚°ã‚¢ã‚¦ãƒˆå®Ÿè¡Œï¼‰ - - ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity, String.class); + URI.create(logoutUrl)); + restTemplate.exchange(requestEntity, String.class); } @Value("#{'${services.keycloak.keycloakUrl}'}") private String keycloakUrl; - // static { - // disableCertificateValidation(); - // } - public void disableCertificateValidation() { + + //TODO Refactor logging + LOGGER.info("disabling certificate validation host : " + keycloakUrl); + // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { @@ -316,18 +277,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { // Ignore differences between given hostname and certificate hostname - HostnameVerifier hv = new HostnameVerifier() { - public boolean verify(String hostname, SSLSession session) { - - // System.out.println("Warning: URL Host: " + hostname + " vs. " - // + session.getPeerHost()); - if (hostname.equals(keycloakUrl) && session.getPeerHost().equals(keycloakUrl)) { - return true; - } else { - return false; - } - } - }; + HostnameVerifier hv = + (hostname, session) -> hostname.equals(keycloakUrl) && session.getPeerHost().equals(keycloakUrl); // Install the all-trusting trust manager try { @@ -336,6 +287,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier(hv); } catch (Exception e) { + // TODO add log message } } diff --git a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java index ff8d70a80..e179b3172 100644 --- a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java +++ b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java @@ -55,7 +55,7 @@ public class SecurityApi { //LOGGER.trace("Cannot read user json", e); } - if (!securityConfiguration.isAuthentication()) { + if (!securityConfiguration.getAuthenticationEnabled()) { if (userInfo.isFakeAuth()) { response.setStatus(401); } @@ -85,7 +85,7 @@ public class SecurityApi { @ConditionalOnExpression("${hbp.authentication.enabled:0}") public void noLogin(HttpServletResponse httpServletResponse) throws IOException { userInfo.setFakeAuth(true); - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /user/login/hbp", "Unathorized login."); + Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /user/login/hbp", "Unauthorized login."); httpServletResponse.sendRedirect(securityConfiguration.getFrontendRedirectAfterLogin()); } @@ -104,7 +104,6 @@ public class SecurityApi { * * @return Return a @{@link ResponseEntity} with the token. */ - @RequestMapping(path = "/galaxy", method = RequestMethod.GET, produces = "application/json") @PreAuthorize("hasRole('Data Manager')") @ResponseStatus(value = HttpStatus.OK) -- GitLab From 83b90cd6bdf9bfbffba8e1bb678128404da01291 Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Tue, 10 Nov 2020 16:33:18 +0200 Subject: [PATCH 11/52] pom.xml cleanup. --- pom.xml | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/pom.xml b/pom.xml index 02129ae2b..817f39ab6 100644 --- a/pom.xml +++ b/pom.xml @@ -63,39 +63,44 @@ </repositories> <dependencies> - <dependency> - <groupId>net.logstash.logback</groupId> - <artifactId>logstash-logback-encoder</artifactId> - <version>6.4</version> - </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> - <groupId>org.springframework.security.oauth.boot</groupId> - <artifactId>spring-security-oauth2-autoconfigure</artifactId> - <version>2.0.1.RELEASE</version> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> - <dependency> - <groupId>org.springframework.data</groupId> - <artifactId>spring-data-commons</artifactId> - </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> + <dependency> + <groupId>org.springframework.security.oauth.boot</groupId> + <artifactId>spring-security-oauth2-autoconfigure</artifactId> + <version>2.0.1.RELEASE</version> + </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-oauth2-resource-server</artifactId> </dependency> <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-web</artifactId> + <groupId>org.springframework.security.oauth</groupId> + <artifactId>spring-security-oauth2</artifactId> + <version>2.5.0.RELEASE</version> + </dependency> + <dependency> + <groupId>net.logstash.logback</groupId> + <artifactId>logstash-logback-encoder</artifactId> + <version>6.4</version> + </dependency> + <dependency> + <groupId>org.springframework.data</groupId> + <artifactId>spring-data-commons</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> @@ -243,11 +248,6 @@ <artifactId>java-jwt</artifactId> <version>3.11.0</version> </dependency> - <dependency> - <groupId>org.springframework.security.oauth</groupId> - <artifactId>spring-security-oauth2</artifactId> - <version>2.5.0.RELEASE</version> - </dependency> </dependencies> <build> -- GitLab From fb4611e74a0ddff34fc674d62e1983a9e6951d83 Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Tue, 10 Nov 2020 08:54:12 -0800 Subject: [PATCH 12/52] Implemented exteption/error handling and added java docs on service layer. --- .../eu/hbp/mip/controllers/ExperimentApi.java | 32 +- .../hbp/mip/controllers/PathologiesApi.java | 3 +- .../eu/hbp/mip/model/DAOs/ExperimentDAO.java | 2 +- .../eu/hbp/mip/model/DTOs/ExperimentDTO.java | 7 +- .../hbp/mip/services/ExperimentService.java | 385 +++++++++++++----- .../services/ExperimentSpecifications.java | 7 +- .../mip/utils/ControllerExceptionHandler.java | 62 ++- .../utils/Exceptions/BadRequestException.java | 8 + .../ExperimentNotFoundException.java | 8 + .../utils/Exceptions/InternalServerError.java | 8 + .../hbp/mip/utils/Exceptions/NoContent.java | 8 + .../Exceptions/UnauthorizedException.java | 8 + .../mip/utils/ResourceNotFoundException.java | 10 - ...ure.sql => V7_0__NewDatabaseStructure.sql} | 0 14 files changed, 423 insertions(+), 125 deletions(-) create mode 100644 src/main/java/eu/hbp/mip/utils/Exceptions/BadRequestException.java create mode 100644 src/main/java/eu/hbp/mip/utils/Exceptions/ExperimentNotFoundException.java create mode 100644 src/main/java/eu/hbp/mip/utils/Exceptions/InternalServerError.java create mode 100644 src/main/java/eu/hbp/mip/utils/Exceptions/NoContent.java create mode 100644 src/main/java/eu/hbp/mip/utils/Exceptions/UnauthorizedException.java delete mode 100644 src/main/java/eu/hbp/mip/utils/ResourceNotFoundException.java rename src/main/resources/db/migration/{V7_0__NewExperimentStructure.sql => V7_0__NewDatabaseStructure.sql} (100%) diff --git a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java index 7cb949a4b..322f33f4f 100644 --- a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java +++ b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java @@ -3,14 +3,18 @@ package eu.hbp.mip.controllers; import eu.hbp.mip.model.DTOs.ExperimentDTO; import eu.hbp.mip.model.UserInfo; import eu.hbp.mip.services.ExperimentService; +import eu.hbp.mip.utils.JsonConverters; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; +import java.util.Map; + import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; /* @@ -39,32 +43,50 @@ public class ExperimentApi { @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "3") int size ) { - return experimentService.getExperiments(name, algorithm, shared, viewed, page, size, "(GET) /experiments"); + Map experiments = experimentService.getExperiments(name, algorithm, shared, viewed, page, size, "(GET) /experiments"); + return new ResponseEntity(experiments, HttpStatus.OK); } + @ApiOperation(value = "Get an experiment", response = ExperimentDTO.class) @RequestMapping(value = "/{uuid}", method = RequestMethod.GET) public ResponseEntity<String> getExperiment( @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - return experimentService.getExperiment(uuid, "(GET) /experiments/{uuid}"); + ExperimentDTO experimentDTO = experimentService.getExperiment(uuid, "(GET) /experiments/{uuid}"); + return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); } + @ApiOperation(value = "Create an experiment", response = ExperimentDTO.class) @RequestMapping(method = RequestMethod.POST) public ResponseEntity<String> createExperiment(Authentication authentication, @RequestBody ExperimentDTO experimentDTO) { - return experimentService.createExperiment(authentication, experimentDTO, "(POST) /experiments"); + experimentDTO = experimentService.createExperiment(authentication, experimentDTO, "(POST) /experiments"); + return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.CREATED); } + + @ApiOperation(value = "Create a transient experiment", response = ExperimentDTO.class) + @RequestMapping(value = "/{transient}",method = RequestMethod.POST) + public ResponseEntity<String> createTransientExperiment(Authentication authentication, @RequestBody ExperimentDTO experimentDTO) { + experimentDTO = experimentService.createTransientExperiment(authentication, experimentDTO, "(POST) /experiments/transient"); + + return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); + } + + @ApiOperation(value = "Update an experiment", response = ExperimentDTO.class) @RequestMapping(value = "/{uuid}", method = RequestMethod.PATCH) public ResponseEntity<String> updateExperiment(@RequestBody ExperimentDTO experimentDTO,@ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - return experimentService.updateExperiment(uuid, experimentDTO, "(PATCH) /experiments/{uuid}"); + experimentDTO = experimentService.updateExperiment(uuid, experimentDTO, "(PATCH) /experiments/{uuid}"); + return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); } + @ApiOperation(value = "Delete an experiment", response = boolean.class) @RequestMapping(value = "/{uuid}", method = RequestMethod.DELETE) public ResponseEntity<String> deleteExperiment( @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - return experimentService.deleteExperiment(uuid, "(DELETE) /experiments/{uuid}"); + experimentService.deleteExperiment(uuid, "(DELETE) /experiments/{uuid}"); + return new ResponseEntity<>(HttpStatus.OK); } } \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java index 9560cc6dd..4ec7c8e36 100644 --- a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java +++ b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java @@ -10,6 +10,7 @@ import eu.hbp.mip.model.DTOs.PathologyDTO; import eu.hbp.mip.model.UserInfo; import eu.hbp.mip.utils.ClaimUtils; import eu.hbp.mip.utils.CustomResourceLoader; +import eu.hbp.mip.utils.Exceptions.BadRequestException; import eu.hbp.mip.utils.InputStreamConverter; import eu.hbp.mip.utils.Logging; import io.swagger.annotations.Api; @@ -56,7 +57,7 @@ public class PathologiesApi { }.getType()); } catch (IOException e) { Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /pathologies", "Unable to load pathologies"); - return ResponseEntity.badRequest().body("The pathologies could not be loaded."); + throw new BadRequestException("The pathologies could not be loaded."); } // If authentication is disabled return everything diff --git a/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java b/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java index 30d6c87da..2ee8e0ba4 100644 --- a/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java +++ b/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java @@ -120,7 +120,7 @@ public class ExperimentDAO { experimentDTO.setName(this.name); experimentDTO.setResult(JsonConverters.convertJsonStringToObject(this.result, ExperimentDTO.ResultDTO.class)); experimentDTO.setShared(this.shared); - experimentDTO.setUuid(this.uuid.toString()); + experimentDTO.setUuid(this.uuid); experimentDTO.setViewed(this.viewed); return experimentDTO; } diff --git a/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java b/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java index e4b9d34a5..64ade455b 100644 --- a/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java +++ b/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java @@ -4,10 +4,11 @@ import eu.hbp.mip.model.DAOs.ExperimentDAO; import java.util.Date; import java.util.List; +import java.util.UUID; public class ExperimentDTO { - private String uuid; + private UUID uuid; private String name; private String createdBy; private Date created; @@ -39,11 +40,11 @@ public class ExperimentDTO { this.algorithm = algorithmDetails.getName(); } - public String getUuid() { + public UUID getUuid() { return uuid; } - public void setUuid(String uuid) { + public void setUuid(UUID uuid) { this.uuid = uuid; } diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index a02af3ab6..2590e1edd 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -6,7 +6,10 @@ import com.github.jmchilton.blend4j.galaxy.WorkflowsClient; import com.github.jmchilton.blend4j.galaxy.beans.Workflow; import com.github.jmchilton.blend4j.galaxy.beans.WorkflowDetails; import com.github.jmchilton.blend4j.galaxy.beans.WorkflowInputDefinition; -import com.google.gson.*; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import eu.hbp.mip.controllers.galaxy.retrofit.RetroFitGalaxyClients; import eu.hbp.mip.controllers.galaxy.retrofit.RetrofitClientInstance; import eu.hbp.mip.model.DAOs.ExperimentDAO; @@ -14,11 +17,14 @@ import eu.hbp.mip.model.DAOs.UserDAO; import eu.hbp.mip.model.DTOs.AlgorithmDTO; import eu.hbp.mip.model.DTOs.ExperimentDTO; import eu.hbp.mip.model.UserInfo; -import eu.hbp.mip.model.galaxy.ErrorResponse; import eu.hbp.mip.model.galaxy.GalaxyWorkflowResult; import eu.hbp.mip.model.galaxy.PostWorkflowToGalaxyDtoResponse; import eu.hbp.mip.repositories.ExperimentRepository; -import eu.hbp.mip.utils.*; +import eu.hbp.mip.utils.ClaimUtils; +import eu.hbp.mip.utils.Exceptions.*; +import eu.hbp.mip.utils.HTTPUtil; +import eu.hbp.mip.utils.JsonConverters; +import eu.hbp.mip.utils.Logging; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; @@ -27,8 +33,6 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; import retrofit2.Call; @@ -63,11 +67,24 @@ public class ExperimentService { private static final Gson gson = new Gson(); - public ResponseEntity<String> getExperiments(String name,String algorithm, Boolean shared,Boolean viewed, int page, int size, String endpoint) { + /** + * The getExperiments will retrieve the experiments from database according to the filters. + * + * @param name is optional, in case it is required to filter the experiments by name + * @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 page is the page that is required to be retrieve + * @param size is the size of each page + * @param endpoint is the endpoint that called the function + * @return a list of mapped experiments + */ + public Map getExperiments(String name,String algorithm, Boolean shared,Boolean viewed, int page, int size, String endpoint) { UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Listing my experiments."); if(size > 10 ) - return ResponseEntity.badRequest().body("Invalid size input, max size is 10."); + throw new BadRequestException("Invalid size input, max size is 10."); + Specification<ExperimentDAO> spec = Specification.where(new ExperimentSpecifications.ExperimentWithName(name)) .and(new ExperimentSpecifications.ExperimentWithAlgorithm(algorithm)) @@ -79,7 +96,7 @@ public class ExperimentService { List<ExperimentDAO> experimentDAOs = pageExperiments.getContent(); if (experimentDAOs.isEmpty()) - return new ResponseEntity<>(HttpStatus.NO_CONTENT); + throw new NoContent("No experiment found with the filters provided."); List<ExperimentDTO> experimentDTOs = new ArrayList<>(); experimentDAOs.forEach(experimentDAO -> experimentDTOs.add(experimentDAO.convertToDTO())); @@ -90,55 +107,64 @@ public class ExperimentService { response.put("totalExperiments", pageExperiments.getTotalElements()); response.put("totalPages", pageExperiments.getTotalPages()); - return new ResponseEntity(response, HttpStatus.OK); + return response; } - public ResponseEntity<String> getExperiment(String uuid, String endpoint) { + /** + * The getExperiment will retrieve the experiment from database according to the input uuid + * + * @param uuid is the id of the experiment to be retrieved + * @param endpoint is the endpoint that called the function + * @return the experiment information that was retrieved from the database + */ + public ExperimentDTO getExperiment(String uuid, String endpoint) { ExperimentDAO experimentDAO; UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Loading Experiment with uuid : " + uuid); - experimentDAO = loadExperiment(uuid).orElseThrow(() -> new ResourceNotFoundException("Not found Experimnet with id = " + uuid)); - - if (experimentDAO == null) { - Logging.LogUserAction(user.getUsername(), endpoint, "Experiment Not found."); - return new ResponseEntity<>("Not found", HttpStatus.NOT_FOUND); - } + experimentDAO = loadExperiment(uuid).orElseThrow(() -> new ExperimentNotFoundException("Not found Experimnet with id = " + uuid)); if (!experimentDAO.isShared() && !experimentDAO.getCreatedBy().getUsername().equals(user.getUsername())) { Logging.LogUserAction(user.getUsername(), endpoint, "Accessing Experiment is unauthorized."); - return new ResponseEntity<>("You don't have access to the experiment.", HttpStatus.UNAUTHORIZED); + throw new UnauthorizedException("You don't have access to the experiment."); } ExperimentDTO experimentDTO = experimentDAO.convertToDTO(); Logging.LogUserAction(user.getUsername(), endpoint, "Experiment was Loaded with uuid : " + uuid + "."); - return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); + return experimentDTO; } - public ResponseEntity<String> createExperiment(Authentication authentication, ExperimentDTO experimentDTO, String endpoint) { + /** + * The createExperiment will create and save an experiment in the database. + * + * @param authentication is the role of the user + * @param experimentDTO is the experiment information + * @param endpoint is the endpoint that called the function + * @return the experiment information which was created + */ + public ExperimentDTO createExperiment(Authentication authentication, ExperimentDTO experimentDTO, String endpoint) { UserDAO user = userInfo.getUser(); //Checking if check (POST) /experiments has proper input. if (checkPostExperimentProperInput(experimentDTO)){ Logging.LogUserAction(user.getUsername(), endpoint, "Invalid input."); - return ResponseEntity.badRequest().body("Please provide proper input."); + throw new BadRequestException("Please provide proper input."); } // Get the type and name of algorithm String algorithmType = experimentDTO.getAlgorithmDetails().getType(); String algorithmName = experimentDTO.getAlgorithmDetails().getName(); StringBuilder parametersLogMessage = new StringBuilder(", Parameters:\n"); - for (AlgorithmDTO.AlgorithmParamDTO params : experimentDTO.getAlgorithmDetails().getParameters()) { - parametersLogMessage - .append(" ") - .append(params.getLabel()) - .append(" -> ") - .append(params.getValue()) - .append("\n"); - } + experimentDTO.getAlgorithmDetails().getParameters().forEach( + params -> parametersLogMessage + .append(" ") + .append(params.getLabel()) + .append(" -> ") + .append(params.getValue()) + .append("\n") ); Logging.LogUserAction(user.getUsername(), endpoint, "Executing " + algorithmName + parametersLogMessage); if (authenticationIsEnabled) { @@ -154,12 +180,12 @@ public class ExperimentService { if (experimentDatasets == null || experimentDatasets.equals("")) { Logging.LogUserAction(user.getUsername(), endpoint, "A dataset should be specified to run an algorithm."); - return ResponseEntity.badRequest().body("Please provide at least one dataset to run the algorithm."); + throw new BadRequestException("Please provide at least one dataset to run the algorithm."); } // --- Validating proper access rights on the datasets --- if (!ClaimUtils.userHasDatasetsAuthorization(user.getUsername(), authentication.getAuthorities(), experimentDatasets)) { - return ResponseEntity.badRequest().body("You are not authorized to use these datasets."); + throw new BadRequestException("You are not authorized to use these datasets."); } } @@ -173,7 +199,93 @@ public class ExperimentService { } } - public ResponseEntity<String> updateExperiment(String uuid, ExperimentDTO experimentDTO, String endpoint) + /** + * The createTransientExperiment will run synchronous a transient experiment into exareme and provide results + * + * @param authentication is the role of the user + * @param experimentDTO is the experiment information + * @param endpoint is the endpoint that called the function + * @return the experiment information which was created + */ + public ExperimentDTO createTransientExperiment(Authentication authentication, ExperimentDTO experimentDTO, String endpoint){ + UserDAO user = userInfo.getUser(); + + //Checking if check (POST) /experiments has proper input. + if (checkPostExperimentProperInput(experimentDTO)){ + Logging.LogUserAction(user.getUsername(), endpoint, + "Invalid input."); + throw new BadRequestException("Please provide proper input."); + } + + // Get the parameters + List<AlgorithmDTO.AlgorithmParamDTO> algorithmParameters + = experimentDTO.getAlgorithmDetails().getParameters(); + + // Get the type and name of algorithm + String algorithmName = experimentDTO.getAlgorithmDetails().getName(); + + if (!loadProperAlgorithms().contains(algorithmName)){ + Logging.LogUserAction(user.getUsername(), endpoint, + "Not proper algorithm."); + throw new BadRequestException("Please provide proper algorithm."); + } + + StringBuilder parametersLogMessage = new StringBuilder(", Parameters:\n"); + experimentDTO.getAlgorithmDetails().getParameters().forEach( + params -> parametersLogMessage + .append(" ") + .append(params.getLabel()) + .append(" -> ") + .append(params.getValue()) + .append("\n") ); + Logging.LogUserAction(user.getUsername(), endpoint, "Executing " + algorithmName + parametersLogMessage); + + if (authenticationIsEnabled) { + // Getting the dataset from the experiment parameters + String experimentDatasets = null; + for (AlgorithmDTO.AlgorithmParamDTO parameter : experimentDTO.getAlgorithmDetails().getParameters()) { + if (parameter.getLabel().equals("dataset")) { + experimentDatasets = parameter.getValue(); + break; + } + } + + if (experimentDatasets == null || experimentDatasets.equals("")) { + Logging.LogUserAction(user.getUsername(), endpoint, + "A dataset should be specified to run an algorithm."); + throw new BadRequestException("Please provide at least one dataset to run the algorithm."); + } + + // --- Validating proper access rights on the datasets --- + if (!ClaimUtils.userHasDatasetsAuthorization(user.getUsername(), authentication.getAuthorities(), experimentDatasets)) { + throw new BadRequestException("You are not authorized to use these datasets."); + } + } + + String body = gson.toJson(algorithmParameters); + String url = queryExaremeUrl + "/" + algorithmName; + Logging.LogUserAction(user.getUsername(), endpoint, "url: " + url + ", body: " + body); + + Logging.LogUserAction(user.getUsername(), endpoint, + "Completed, returning: " + experimentDTO.toString()); + + // Results are stored in the experiment object + ExaremeResult exaremeResult = runExaremeExperiment(url, body, experimentDTO); + experimentDTO.setResult(exaremeResult.result); + experimentDTO.setStatus((exaremeResult.code>= 400)? ExperimentDAO.Status.error: ExperimentDAO.Status.success); + + return experimentDTO; + } + + /** + * The updateExperiment will update the experiment's properties + * + * @param uuid is the id of the experiment to be updated + * @param experimentDTO is the experiment information to be updated + * @param endpoint is the endpoint that called the function + * @return the updated experiment information + */ + public ExperimentDTO updateExperiment(String uuid, ExperimentDTO experimentDTO, String endpoint) { ExperimentDAO experimentDAO; UserDAO user = userInfo.getUser(); @@ -182,7 +294,7 @@ public class ExperimentService { if (checkPutExperimentProperInput(experimentDTO)){ Logging.LogUserAction(user.getUsername(), endpoint, "Invalid input."); - return ResponseEntity.badRequest().body("Please provide proper input."); + throw new BadRequestException("Please provide proper input."); } if((experimentDTO.getName() == null || experimentDTO.getName().length() == 0) @@ -190,13 +302,13 @@ public class ExperimentService { && experimentDTO.getViewed() == null && experimentDTO.getAlgorithmDetails() == null) { - return ResponseEntity.badRequest().body("Input is required."); + throw new BadRequestException("Input is required."); } - experimentDAO = loadExperiment(uuid).get(); + experimentDAO = loadExperiment(uuid).orElseThrow(() -> new ExperimentNotFoundException("Not found Experimnet with id = " + uuid)); if (!experimentDAO.getCreatedBy().getUsername().equals(user.getUsername())) - return new ResponseEntity<>("You're not the owner of this experiment", HttpStatus.UNAUTHORIZED); + throw new UnauthorizedException("You don't have access to the experiment."); if(experimentDTO.getName() != null && experimentDTO.getName().length() != 0) { @@ -213,29 +325,39 @@ public class ExperimentService { experimentDAO.setViewed(experimentDTO.getViewed()); } - experimentRepository.save(experimentDAO); - + try { + experimentRepository.save(experimentDAO); + } + catch (Exception e){ + Logging.LogUserAction(user.getUsername(), endpoint, "Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); + throw new InternalServerError(e.getMessage()); + } Logging.LogUserAction(user.getUsername(), endpoint, "Updated experiment with uuid : " + experimentDTO.getUuid() + "."); experimentDTO = experimentDAO.convertToDTO(); - return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); + return experimentDTO; } - public ResponseEntity<String> deleteExperiment(String uuid, String endpoint) + /** + * The deleteExperiment will delete an experiment from the database + * + * @param uuid is the id of the experiment to be deleted + * @param endpoint is the endpoint that called the function + */ + public void deleteExperiment(String uuid, String endpoint) { ExperimentDAO experimentDAO; UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Deleting experiment with uuid : " + uuid + "."); - experimentDAO = loadExperiment(uuid).get(); + experimentDAO = loadExperiment(uuid).orElseThrow(() -> new ExperimentNotFoundException("Not found Experimnet with id = " + uuid)); if (!experimentDAO.getCreatedBy().getUsername().equals(user.getUsername())) - return new ResponseEntity<>(endpoint, HttpStatus.UNAUTHORIZED); + throw new UnauthorizedException("You don't have access to the experiment."); experimentRepository.delete(experimentDAO); Logging.LogUserAction(user.getUsername(), endpoint, "Deleted experiment with uuid : " + uuid + "."); - return new ResponseEntity<>(HttpStatus.NO_CONTENT); } // /* ------------------------------- PRIVATE METHODS ----------------------------------------------------*/ @@ -250,6 +372,14 @@ public class ExperimentService { || experimentDTO.getUuid() != null; } + private List<String> loadProperAlgorithms() + { + List<String> properAlgorithms = new ArrayList<>(); + properAlgorithms.add("histograms"); + properAlgorithms.add("descriptive_stats"); + return properAlgorithms; + } + private boolean checkPutExperimentProperInput(ExperimentDTO experimentDTO) { return experimentDTO.getUuid() != null @@ -258,21 +388,29 @@ public class ExperimentService { || experimentDTO.getStatus() != null; } + /** + * The loadExperiment access the database and load the information of a specific experiment + * + * @param uuid is the id of the experiment to be retrieved + * @return the experiment information that was retrieved from database + */ private Optional<ExperimentDAO> loadExperiment(String uuid){ UUID experimentUuid ; - try { - experimentUuid = UUID.fromString(uuid); - } catch (IllegalArgumentException iae) { - return null; - } + experimentUuid = Optional.of(UUID.fromString(uuid)).orElseThrow(() -> new IllegalArgumentException("Invalid input uuid:"+ uuid)); return experimentRepository.findByUuid(experimentUuid); } - private ExperimentDAO createExperiment(ExperimentDTO experimentDTO, String endpoint) { + /** + * The createExperimentInTheDatabase will insert a new experiment in the database according to the given experiment information + * + * @param experimentDTO is the experiment information to inserted in the database + * @return the experiment information that was inserted into the database + */ + private ExperimentDAO createExperimentInTheDatabase(ExperimentDTO experimentDTO, String endpoint) { UserDAO user = userInfo.getUser(); ExperimentDAO experimentDAO = new ExperimentDAO(); @@ -281,7 +419,14 @@ public class ExperimentService { experimentDAO.setAlgorithmDetails(JsonConverters.convertObjectToJsonString(experimentDTO.getAlgorithmDetails())); experimentDAO.setAlgorithm(experimentDTO.getAlgorithm()); experimentDAO.setName(experimentDTO.getName()); - experimentRepository.save(experimentDAO); + experimentDAO.setStatus(ExperimentDAO.Status.pending); + try { + experimentRepository.save(experimentDAO); + } + catch (Exception e){ + Logging.LogUserAction(user.getUsername(), endpoint, "Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); + throw new InternalServerError(e.getMessage()); + } Logging.LogUserAction(user.getUsername(), endpoint, " id : " + experimentDAO.getUuid()); Logging.LogUserAction(user.getUsername(), endpoint, " algorithms : " + experimentDAO.getAlgorithmDetails()); @@ -298,29 +443,43 @@ public class ExperimentService { Logging.LogUserAction(user.getUsername(), endpoint, " historyId : " + experimentDAO.getWorkflowHistoryId()); Logging.LogUserAction(user.getUsername(), endpoint, " status : " + experimentDAO.getStatus()); - experimentRepository.save(experimentDAO); + try { + experimentRepository.save(experimentDAO); + } + catch (Exception e){ + Logging.LogUserAction(user.getUsername(), endpoint, "Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); + throw new InternalServerError(e.getMessage()); + } Logging.LogUserAction(user.getUsername(), endpoint, "Saved experiment"); } - private void finishExperiment(ExperimentDAO experimentDAO) { + private void finishExperiment(ExperimentDAO experimentDAO, String endpoint) { experimentDAO.setFinished(new Date()); - experimentRepository.save(experimentDAO); + try { + experimentRepository.save(experimentDAO); + } + catch (Exception e){ + Logging.LogUserAction(userInfo.getUser().getUsername(), endpoint, "Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); + throw new InternalServerError(e.getMessage()); + } } /* -------------------------------------- EXAREME CALLS ---------------------------------------------------------*/ /** - * The runExaremeExperiment will POST the algorithm to the exareme client + * The createExaremeExperiment will POST the algorithm to the exareme client * * @param experimentDTO is the request with the experiment information - * @return the response to be returned + * @param endpoint is the endpoint that called the function + * @return the experiment information that was retrieved from exareme */ - public ResponseEntity<String> createExaremeExperiment(ExperimentDTO experimentDTO, String endpoint) { + public ExperimentDTO createExaremeExperiment(ExperimentDTO experimentDTO, String endpoint) { + UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Running the algorithm..."); - ExperimentDAO experimentDAO = createExperiment(experimentDTO, endpoint); + ExperimentDAO experimentDAO = createExperimentInTheDatabase(experimentDTO, endpoint); Logging.LogUserAction(user.getUsername(), endpoint, "Created experiment with uuid :" + experimentDAO.getUuid()); // Run the 1st algorithm from the list @@ -339,41 +498,57 @@ public class ExperimentService { Logging.LogUserAction(user.getUsername(), endpoint, "Starting exareme execution thread"); + ExperimentDTO finalExperimentDTO = experimentDTO; new Thread(() -> { // ATTENTION: Inside the Thread only LogExperimentAction should be used, not LogUserAction! Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Thread named :" + Thread.currentThread().getName() + " with id :" + Thread.currentThread().getId() + " started!"); try { - - // --> New function runExaremeAlgorithm - StringBuilder results = new StringBuilder(); - int code = HTTPUtil.sendPost(url, body, results); - - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Algorithm finished with code: " + code); - // Results are stored in the experiment object - ExperimentDTO.ResultDTO resultDTO = JsonConverters.convertJsonStringToObject(String.valueOf(results), ExperimentDTO.ResultDTO.class); + ExaremeResult exaremeResult = runExaremeExperiment(url, body, finalExperimentDTO); - // <-- New function runExaremeAlgorithm - - experimentDAO.setResult(JsonConverters.convertObjectToJsonString(resultDTO)); -// experimentDAO.setHasError(code >= 400); -// experimentDAO.setHasServerError(code >= 500); + experimentDAO.setResult(JsonConverters.convertObjectToJsonString(exaremeResult.result)); + experimentDAO.setStatus((exaremeResult.code>= 400)? ExperimentDAO.Status.error: ExperimentDAO.Status.success); } catch (Exception e) { Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "There was an exception: " + e.getMessage()); experimentDAO.setStatus(ExperimentDAO.Status.error); - experimentDAO.setResult(e.getMessage()); } - finishExperiment(experimentDAO); + finishExperiment(experimentDAO, endpoint); Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Finished the experiment: " + experimentDAO.toString()); }).start(); experimentDTO = experimentDAO.convertToDTO(); - return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.CREATED); + return experimentDTO; } + /** + * The runExaremeExperiment will run to exareme the experiment + * + * @param url is the url that contain the results of the experiment + * @param body is the parameters of the algorithm + * @param experimentDTO is the experiment information to be executed in the exareme + * @return the result of exareme as well as the http status that was retrieved + */ + public ExaremeResult runExaremeExperiment(String url,String body, ExperimentDTO experimentDTO) { + + StringBuilder results = new StringBuilder(); + int code; + try { + code = HTTPUtil.sendPost(url, body, results); + } + catch (Exception e){ + throw new InternalServerError("Error occured : "+ e.getMessage()); + } + Logging.LogExperimentAction(experimentDTO.getName(), experimentDTO.getUuid(), "Algorithm finished with code: " + code); + + // Results are stored in the experiment object + ExperimentDTO.ResultDTO resultDTO = JsonConverters.convertJsonStringToObject(String.valueOf(results), ExperimentDTO.ResultDTO.class); + return new ExaremeResult(code, resultDTO); + } + + /* --------------------------------------- GALAXY CALLS ---------------------------------------------------------*/ @@ -383,11 +558,11 @@ public class ExperimentService { * @param experimentDTO is the request with the experiment information * @return the response to be returned */ - public ResponseEntity<String> runGalaxyWorkflow(ExperimentDTO experimentDTO, String endpoint) { + public ExperimentDTO runGalaxyWorkflow(ExperimentDTO experimentDTO, String endpoint) { UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Running a workflow..."); - ExperimentDAO experimentDAO = createExperiment(experimentDTO, endpoint); + ExperimentDAO experimentDAO = createExperimentInTheDatabase(experimentDTO, endpoint); Logging.LogUserAction(user.getUsername(), endpoint, "Created experiment with uuid :" + experimentDAO.getUuid()); @@ -419,8 +594,7 @@ public class ExperimentService { if (workflow == null) { Logging.LogUserAction(user.getUsername(), endpoint, "Could not find algorithm code: " + workflowId); - return ResponseEntity.badRequest() - .body(new ErrorResponse("Could not find galaxy algorithm.").toString()); + throw new BadRequestException("Could not find galaxy algorithm."); } final WorkflowDetails workflowDetails = workflowsClient.showWorkflow(workflow.getId()); for (Map.Entry<String, WorkflowInputDefinition> workflowParameter : workflowDetails.getInputs().entrySet()) { @@ -449,8 +623,7 @@ public class ExperimentService { String historyId = (String) new JSONObject(responseBody).get("history_id"); experimentDAO.setWorkflowHistoryId(historyId); - experimentDAO.setStatus(ExperimentDAO.Status.pending); -// experimentDAO.setHasServerError(response.code() >= 500); + experimentDAO.setStatus(ExperimentDAO.Status.success); } else { // Something unexpected happened String msgErr = gson.toJson(response.errorBody()); @@ -460,15 +633,12 @@ public class ExperimentService { JSONObject jObjectError = new JSONObject(msgErr); String errMsg = jObjectError.get("err_msg").toString(); - experimentDAO.setResult(errMsg); -// experimentDAO.setHasError(response.code() >= 400); -// experimentDAO.setHasServerError(response.code() >= 500); + experimentDTO.setStatus((response.code()>= 400)? ExperimentDAO.Status.error: ExperimentDAO.Status.success); } } catch (Exception e) { Logging.LogUserAction(user.getUsername(), endpoint, "An exception occurred: " + e.getMessage()); experimentDAO.setStatus(ExperimentDAO.Status.error); - experimentDAO.setResult(e.getMessage()); } saveExperiment(experimentDAO, endpoint); @@ -478,7 +648,7 @@ public class ExperimentService { Logging.LogUserAction(user.getUsername(), endpoint, "Run workflow completed!"); experimentDTO = experimentDAO.convertToDTO(); - return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.CREATED); + return experimentDTO; } @@ -543,19 +713,20 @@ public class ExperimentService { if (result == null) { experimentDAO.setStatus(ExperimentDAO.Status.error); } - experimentDAO.setResult("[" + result + "]"); - experimentDAO.setStatus(ExperimentDAO.Status.success); - resultFound = true; + else { + experimentDAO.setResult("[" + result + "]"); + experimentDAO.setStatus(ExperimentDAO.Status.success); + resultFound = true; + } } } if (!resultFound) { // If there is no visible result Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "No visible result"); - experimentDAO.setResult("[" + new ErrorResponse("The workflow has no visible result.").toString() + "]"); experimentDAO.setStatus(ExperimentDAO.Status.error); } - finishExperiment(experimentDAO); + finishExperiment(experimentDAO, endpoint); break; case "error": @@ -574,7 +745,6 @@ public class ExperimentService { if (result == null) { experimentDAO.setStatus(ExperimentDAO.Status.error); } - experimentDAO.setResult("[" + result + "]"); experimentDAO.setStatus(ExperimentDAO.Status.error); failedJobFound = true; } @@ -582,16 +752,15 @@ public class ExperimentService { if (!failedJobFound) { // If there is no visible failed job Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "No failed result"); - experimentDAO.setResult("[" + new ErrorResponse("The workflow has no failed result.").toString() + "]"); experimentDAO.setStatus(ExperimentDAO.Status.error); } - finishExperiment(experimentDAO); + finishExperiment(experimentDAO, endpoint); break; default: // InternalError or unexpected result - experimentDAO.setResult("[" + new ErrorResponse("An unexpected error occurred.").toString() + "]"); + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "An unexpected error occurred."); experimentDAO.setStatus(ExperimentDAO.Status.error); - finishExperiment(experimentDAO); + finishExperiment(experimentDAO, endpoint); break; } @@ -624,7 +793,7 @@ public class ExperimentService { RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); Call<Object> call = service.getWorkflowStatusFromGalaxy(historyId, galaxyApiKey); - String result = null; + String result; try { Response<Object> response = call.execute(); if (response.code() >= 400) { @@ -640,7 +809,7 @@ public class ExperimentService { return "internalError"; } - String state = null; + String state; try { JSONObject resultJson = new JSONObject(result); state = resultJson.getString("state"); @@ -679,7 +848,7 @@ public class ExperimentService { RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); Call<List<GalaxyWorkflowResult>> call = service.getWorkflowResultsFromGalaxy(historyId, galaxyApiKey); - List<GalaxyWorkflowResult> getGalaxyWorkflowResultList = null; + List<GalaxyWorkflowResult> getGalaxyWorkflowResultList; try { Response<List<GalaxyWorkflowResult>> response = call.execute(); if (response.code() >= 400) { @@ -717,7 +886,7 @@ public class ExperimentService { Call<Object> call = service.getWorkflowResultsBodyFromGalaxy(historyId, contentId, galaxyApiKey); - String resultJson = null; + String resultJson ; try { Response<Object> response = call.execute(); if (response.code() >= 400) { @@ -751,8 +920,8 @@ public class ExperimentService { RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); Call<Object> callError = service.getErrorMessageOfWorkflowFromGalaxy(jobId, galaxyApiKey); - String fullError = null; - String returnError = null; + String fullError; + String returnError; try { Response<Object> response = callError.execute(); if (response.code() >= 400) { @@ -782,4 +951,22 @@ public class ExperimentService { return returnError; } + + final class ExaremeResult { + private final int code; + private final ExperimentDTO.ResultDTO result; + + public ExaremeResult(int code, ExperimentDTO.ResultDTO result) { + this.code = code; + this.result = result; + } + + public int getCode() { + return code; + } + + public ExperimentDTO.ResultDTO getResult() { + return result; + } + } } diff --git a/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java b/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java index 6596a319a..076249e0b 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java @@ -16,7 +16,7 @@ public class ExperimentSpecifications { public ExperimentWithName(String name){ this.name = name; - this.regExp = "%"+name+"%"; + this.regExp = name; } public Predicate toPredicate(Root<ExperimentDAO> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) @@ -24,8 +24,11 @@ public class ExperimentSpecifications { if (name == null) { return cb.isTrue(cb.literal(true)); } + else { + regExp = (name.contains("%")?name:name+"%"); + } - return cb.like( root.get( "name" ), regExp ); + return cb.like( root.get( "name" ), this.regExp ); } } public static class ExperimentWithAlgorithm implements Specification<ExperimentDAO> { diff --git a/src/main/java/eu/hbp/mip/utils/ControllerExceptionHandler.java b/src/main/java/eu/hbp/mip/utils/ControllerExceptionHandler.java index 4e6f8da32..7f644fb7f 100644 --- a/src/main/java/eu/hbp/mip/utils/ControllerExceptionHandler.java +++ b/src/main/java/eu/hbp/mip/utils/ControllerExceptionHandler.java @@ -1,26 +1,80 @@ package eu.hbp.mip.utils; +import eu.hbp.mip.utils.Exceptions.*; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; import java.util.Date; @ControllerAdvice -public class ControllerExceptionHandler { +public class ControllerExceptionHandler extends ResponseEntityExceptionHandler { + + @ExceptionHandler(ExperimentNotFoundException.class) + public ResponseEntity<Object> handleExperimentNotFoundException(ExperimentNotFoundException ex, WebRequest request) { - @ExceptionHandler(ResourceNotFoundException.class) - public ResponseEntity<ErrorMessage> resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) { ErrorMessage message = new ErrorMessage( - 2, + HttpStatus.NOT_FOUND.value(), new Date(), ex.getMessage(), request.getDescription(false)); return new ResponseEntity<>(message, HttpStatus.NOT_FOUND); } + + @ExceptionHandler(BadRequestException.class) + public ResponseEntity<Object> handleBadRequestException(BadRequestException ex, WebRequest request) { + + ErrorMessage message = new ErrorMessage( + HttpStatus.BAD_REQUEST.value(), + new Date(), + ex.getMessage(), + request.getDescription(false)); + + return new ResponseEntity<>(message, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(UnauthorizedException.class) + public ResponseEntity<Object> handleUnauthorizedException(UnauthorizedException ex, WebRequest request) { + + ErrorMessage message = new ErrorMessage( + HttpStatus.UNAUTHORIZED.value(), + new Date(), + ex.getMessage(), + request.getDescription(false)); + + return new ResponseEntity<>(message, HttpStatus.UNAUTHORIZED); + } + + @ExceptionHandler(IllegalArgumentException.class) + public Object handleIllegalArgumentException(IllegalArgumentException ex, WebRequest request) { + return null; + } + + @ExceptionHandler(InternalServerError.class) + public ResponseEntity<ErrorMessage> handleInternalServerError(InternalServerError er, WebRequest request) { + ErrorMessage message = new ErrorMessage( + HttpStatus.INTERNAL_SERVER_ERROR.value(), + new Date(), + er.getMessage(), + request.getDescription(false)); + + return new ResponseEntity<>(message, HttpStatus.INTERNAL_SERVER_ERROR); + } + + @ExceptionHandler(NoContent.class) + public ResponseEntity<ErrorMessage> handleNoContent(NoContent nc, WebRequest request) { + ErrorMessage message = new ErrorMessage( + HttpStatus.NO_CONTENT.value(), + new Date(), + nc.getMessage(), + request.getDescription(false)); + + return new ResponseEntity<>(message, HttpStatus.NO_CONTENT); + } @ExceptionHandler(Exception.class) public ResponseEntity<ErrorMessage> globalExceptionHandler(Exception ex, WebRequest request) { diff --git a/src/main/java/eu/hbp/mip/utils/Exceptions/BadRequestException.java b/src/main/java/eu/hbp/mip/utils/Exceptions/BadRequestException.java new file mode 100644 index 000000000..6684cae28 --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/Exceptions/BadRequestException.java @@ -0,0 +1,8 @@ +package eu.hbp.mip.utils.Exceptions; + +public class BadRequestException extends RuntimeException { + + public BadRequestException(String msg) { + super(msg); + } +} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/utils/Exceptions/ExperimentNotFoundException.java b/src/main/java/eu/hbp/mip/utils/Exceptions/ExperimentNotFoundException.java new file mode 100644 index 000000000..b8e05d7e6 --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/Exceptions/ExperimentNotFoundException.java @@ -0,0 +1,8 @@ +package eu.hbp.mip.utils.Exceptions; + +public class ExperimentNotFoundException extends RuntimeException { + + public ExperimentNotFoundException(String msg) { + super(msg); + } +} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/utils/Exceptions/InternalServerError.java b/src/main/java/eu/hbp/mip/utils/Exceptions/InternalServerError.java new file mode 100644 index 000000000..19bc88eed --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/Exceptions/InternalServerError.java @@ -0,0 +1,8 @@ +package eu.hbp.mip.utils.Exceptions; + +public class InternalServerError extends RuntimeException { + + public InternalServerError(String msg) { + super(msg); + } +} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/utils/Exceptions/NoContent.java b/src/main/java/eu/hbp/mip/utils/Exceptions/NoContent.java new file mode 100644 index 000000000..9bb67d811 --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/Exceptions/NoContent.java @@ -0,0 +1,8 @@ +package eu.hbp.mip.utils.Exceptions; + +public class NoContent extends RuntimeException { + + public NoContent(String msg) { + super(msg); + } +} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/utils/Exceptions/UnauthorizedException.java b/src/main/java/eu/hbp/mip/utils/Exceptions/UnauthorizedException.java new file mode 100644 index 000000000..379911dae --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/Exceptions/UnauthorizedException.java @@ -0,0 +1,8 @@ +package eu.hbp.mip.utils.Exceptions; + +public class UnauthorizedException extends RuntimeException { + + public UnauthorizedException(String msg) { + super(msg); + } +} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/utils/ResourceNotFoundException.java b/src/main/java/eu/hbp/mip/utils/ResourceNotFoundException.java deleted file mode 100644 index 38f067c42..000000000 --- a/src/main/java/eu/hbp/mip/utils/ResourceNotFoundException.java +++ /dev/null @@ -1,10 +0,0 @@ -package eu.hbp.mip.utils; - -public class ResourceNotFoundException extends RuntimeException { - - private static final long serialVersionUID = 1L; - - public ResourceNotFoundException(String msg) { - super(msg); - } -} \ No newline at end of file diff --git a/src/main/resources/db/migration/V7_0__NewExperimentStructure.sql b/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql similarity index 100% rename from src/main/resources/db/migration/V7_0__NewExperimentStructure.sql rename to src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql -- GitLab From 12f91d4474bf14de071f517d6c4ba6779a56468f Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Tue, 10 Nov 2020 20:44:45 +0200 Subject: [PATCH 13/52] Added spring debug level. --- docker/config/application.tmpl | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index 190f04f90..324b73432 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -41,6 +41,7 @@ frontend: logging: level: root: {{ default .Env.LOG_LEVEL_FRAMEWORK "ERROR" }} + org: {{ default .Env.LOG_LEVEL_FRAMEWORK "ERROR" }} eu: hbp: {{ default .Env.LOG_LEVEL "INFO" }} -- GitLab From 61d3ef9094ebcebca75cccf8de789c9b563e6331 Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Wed, 11 Nov 2020 05:19:52 -0800 Subject: [PATCH 14/52] Fixing 404 message on login as well as on swagger. --- docker/config/application.tmpl | 3 ++- pom.xml | 4 ++++ src/main/java/eu/hbp/mip/configuration/WebConfiguration.java | 2 -- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index 190f04f90..9496499da 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -46,7 +46,8 @@ logging: # EMBEDDED SERVER CONFIGURATION server: - contextPath: {{ default .Env.CONTEXT_PATH "/services" }} + servlet: + contextPath: {{ default .Env.CONTEXT_PATH "/services" }} port: 8080 use-forward-headers: true session: diff --git a/pom.xml b/pom.xml index 817f39ab6..e08146ef2 100644 --- a/pom.xml +++ b/pom.xml @@ -67,6 +67,10 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-data-rest</artifactId> + </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> diff --git a/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java b/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java index 17e38a5ee..72bcad789 100644 --- a/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java @@ -2,7 +2,6 @@ package eu.hbp.mip.configuration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.data.web.config.EnableSpringDataWebSupport; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; @@ -17,7 +16,6 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration @EnableSwagger2 -@EnableSpringDataWebSupport public class WebConfiguration { // @Bean -- GitLab From 2c515aaf748c79de34276975dc799d27a4ce15ee Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Fri, 6 Nov 2020 12:53:30 +0200 Subject: [PATCH 15/52] Configuring keycloak security. --- Dockerfile | 7 +- docker/README.md | 4 +- docker/config/application.tmpl | 46 +-- pom.xml | 51 ++- .../configuration/SecurityConfiguration.java | 365 ++++++++---------- .../eu/hbp/mip/controllers/SecurityApi.java | 7 +- .../java/eu/hbp/mip/model/DAOs/UserDAO.java | 48 ++- src/main/java/eu/hbp/mip/model/UserInfo.java | 20 +- 8 files changed, 242 insertions(+), 306 deletions(-) diff --git a/Dockerfile b/Dockerfile index 458fbc8a3..866c01bf4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,17 +43,16 @@ WORKDIR /home/portal ENTRYPOINT ["/run.sh"] # 8080: Web service API, health checks on http://host:8080$CONTEXT_PATH/health -# 4089: Akka cluster -EXPOSE 4089 8080 +EXPOSE 8080 HEALTHCHECK --start-period=60s CMD curl -v --silent http://localhost:8080$CONTEXT_PATH/health 2>&1 | grep UP LABEL org.label-schema.build-date=$BUILD_DATE \ org.label-schema.name="hbpmip/portal-backend" \ - org.label-schema.description="Java backend for the MIP portal" \ + org.label-schema.description="Spring backend for the MIP portal" \ org.label-schema.url="https://mip.humanbrainproject.eu" \ org.label-schema.vcs-type="git" \ - org.label-schema.vcs-url="https://github.com/LREN-CHUV/portal-backend" \ + org.label-schema.vcs-url="https://github.com/HBPMedical/portal-backend" \ org.label-schema.vcs-ref=$VCS_REF \ org.label-schema.version="$VERSION" \ org.label-schema.vendor="LREN CHUV" \ diff --git a/docker/README.md b/docker/README.md index 3f3437aec..cc8c59fe1 100644 --- a/docker/README.md +++ b/docker/README.md @@ -72,4 +72,6 @@ To use this image, you need a running instance of PostgreSQL and to configure th * RELEASE_STAGE: Release stage used when reporting errors to Bugsnag. Values are dev, staging, production * DATA_CENTER_LOCATION: Location of the datacenter, used when reporting errors to Bugsnag -* CONTAINER_ORCHESTRATION: Container orchestration system used to execute the Docker containers. Values are mesos, docker-compose, kubernetes + + +# TODO Refactor variables \ No newline at end of file diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index 4e723b8a3..84ef19c5e 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -18,38 +18,12 @@ spring: dialect: org.hibernate.dialect.PostgreSQL9Dialect ddl-auto: validate - # SPRING RESOURCES HANDLING - resources: - chain: - enabled: true # TODO: why is that enabled? Do we serve any resources from the backend? - -# HBP OAUTH2 LOGIN -hbp: - authentication: - enabled: {{ default .Env.AUTHENTICATION "1" }} - client: - clientId: {{ default .Env.CLIENT_ID "996f97c5-a3ca-460e-b18b-00df3e2be89a" }} - clientSecret: {{ .Env.CLIENT_SECRET }} - accessTokenUri: {{ default .Env.TOKEN_URI "https://services.humanbrainproject.eu/oidc/token" }} - userAuthorizationUri: {{ default .Env.AUTH_URI "https://services.humanbrainproject.eu/oidc/authorize" }} - logoutUri: {{ .Env.LOGOUT_URI }} - tokenName: access_token - authenticationScheme: query - clientAuthenticationScheme: form - useCurrentUri: false - preEstablishedRedirectUri: {{ default .Env.FRONTEND_LOGIN_URL "http://frontend/services/login/hbp" }} - resource: - userInfoUri: {{ default .Env.USER_INFO_URI "https://services.humanbrainproject.eu/oidc/userinfo" }} - revokeTokenUri: {{ default .Env.REVOKE_TOKEN_URI "https://services.humanbrainproject.eu/oidc/slo" }} - sso: - login-path: - # WEB FRONTEND frontend: loginUrl: {{ default .Env.FRONTEND_LOGIN_URL "http://frontend/services/login/hbp" }} + redirectAfterLoginUrl: {{ default .Env.FRONTEND_AFTER_LOGIN_URL "http://frontend/" }} redirectAfterLogoutUrl: {{ default .Env.FRONTEND_AFTER_LOGOUT_URL (default .Env.LOGIN_URI "http://frontend/services/login/hbp") }} - redirectAfterLoginUrl: {{ default .Env.FRONTEND_AFTER_LOGIN_URL "http://frontend/home" }} - + logging: level: root: {{ default .Env.LOG_LEVEL_FRAMEWORK "ERROR" }} @@ -72,6 +46,7 @@ endpoints: endpoint: /health sensitive: false +# External Services services: exareme: queryExaremeUrl: {{ default .Env.EXAREME_URL "http://localhost:9090" }}/mining/query @@ -84,5 +59,16 @@ services: galaxyUsername: {{ default .Env.GALAXY_USERNAME "admin" }} galaxyPassword: {{ default .Env.GALAXY_PASSWORD "password" }} - keycloak: - keycloakUrl: {{ .Env.KEYCLOAK_URL }} +# HBP Authentication +hbp: + authentication: + enabled: {{ default .Env.AUTHENTICATION "1" }} + +# Keycloak +keycloak: + auth-server-url: {{ .Env.KEYCLOAK_URL }} + realm: {{ .Env.KEYCLOAK_REALM }} + resource: {{ .Env.CLIENT_ID }} + credentials: + secret: {{ .Env.CLIENT_SECRET }} + # logoutUrl: {{ .Env.LOGOUT_URL }} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 02129ae2b..a856ee073 100644 --- a/pom.xml +++ b/pom.xml @@ -18,11 +18,9 @@ </parent> <properties> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <sonar.host.url>https://hbps1.chuv.ch/sonarqube/</sonar.host.url> - <sonar.projectName>MIP portal-backend</sonar.projectName> - <sonar.sources>src/main/java/</sonar.sources> <java.version>1.8</java.version> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <keycloak-spring-boot-starter.version>11.0.2</keycloak-spring-boot-starter.version> <springfox-swagger.version>2.5.0</springfox-swagger.version> <spring-context.version>5.2.10.RELEASE</spring-context.version> <springfox-boot-starter.version>3.0.0</springfox-boot-starter.version> @@ -63,40 +61,46 @@ </repositories> <dependencies> - <dependency> - <groupId>net.logstash.logback</groupId> - <artifactId>logstash-logback-encoder</artifactId> - <version>6.4</version> - </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> - <groupId>org.springframework.security.oauth.boot</groupId> - <artifactId>spring-security-oauth2-autoconfigure</artifactId> - <version>2.0.1.RELEASE</version> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> - <dependency> - <groupId>org.springframework.data</groupId> - <artifactId>spring-data-commons</artifactId> - </dependency> <dependency> <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-security</artifactId> + <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> - <groupId>org.springframework.security</groupId> - <artifactId>spring-security-oauth2-resource-server</artifactId> + <groupId>org.springframework.data</groupId> + <artifactId>spring-data-commons</artifactId> </dependency> <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-web</artifactId> + <groupId>org.keycloak</groupId> + <artifactId>keycloak-spring-boot-starter</artifactId> + <version>${keycloak-spring-boot-starter.version}</version> </dependency> + +<!-- <dependency>--> +<!-- <groupId>org.springframework.security</groupId>--> +<!-- <artifactId>spring-security-oauth2-resource-server</artifactId>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>org.springframework.security.oauth.boot</groupId>--> +<!-- <artifactId>spring-security-oauth2-autoconfigure</artifactId>--> +<!-- <version>2.0.1.RELEASE</version>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>org.springframework.security.oauth</groupId>--> +<!-- <artifactId>spring-security-oauth2</artifactId>--> +<!-- <version>2.5.0.RELEASE</version>--> +<!-- </dependency>--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> @@ -243,11 +247,6 @@ <artifactId>java-jwt</artifactId> <version>3.11.0</version> </dependency> - <dependency> - <groupId>org.springframework.security.oauth</groupId> - <artifactId>spring-security-oauth2</artifactId> - <version>2.5.0.RELEASE</version> - </dependency> </dependencies> <build> diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index afd3e56ed..68a1ca4a1 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -1,42 +1,30 @@ package eu.hbp.mip.configuration; -import eu.hbp.mip.model.UserInfo; -import eu.hbp.mip.utils.*; +import eu.hbp.mip.utils.CORSFilter; +import eu.hbp.mip.utils.CustomAccessDeniedHandler; +import eu.hbp.mip.utils.CustomLoginUrlAuthenticationEntryPoint; +import org.keycloak.adapters.KeycloakConfigResolver; +import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; +import org.keycloak.adapters.springsecurity.KeycloakConfiguration; +import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; +import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.security.oauth2.resource.AuthoritiesExtractor; -import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; -import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.*; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.oauth2.client.OAuth2ClientContext; -import org.springframework.security.oauth2.client.OAuth2RestTemplate; -import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter; -import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter; -import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails; -import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; +import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; +import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.web.access.channel.ChannelProcessingFilter; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; -import org.springframework.security.web.authentication.logout.LogoutHandler; -import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; +import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy; +import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; +import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; import org.springframework.security.web.csrf.CsrfFilter; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.csrf.CsrfTokenRepository; import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.WebUtils; @@ -48,130 +36,133 @@ import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import java.net.URI; import java.security.SecureRandom; import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -// See https://spring.io/guides/tutorials/spring-boot-oauth2/ for reference about configuring OAuth2 login +// Reference for OAuth2 login: https://spring.io/guides/tutorials/spring-boot-oauth2/ // also http://cscarioni.blogspot.ch/2013/04/pro-spring-security-and-oauth-2.html +// Security with Keycloak: https://www.thomasvitale.com/keycloak-authentication-flow-sso-client/ -@Configuration -@EnableOAuth2Client -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { +@KeycloakConfiguration +public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfiguration.class); - @Autowired - private OAuth2ClientContext oauth2ClientContext; - /** * Enable HBP collab authentication (1) or disable it (0). Default is 1 */ - @Value("#{'${hbp.authentication.enabled:1}'}") - private boolean authentication; + @Value("#{'${hbp.authentication.enabled}'}") + private boolean authenticationEnabled; /** * Absolute URL to redirect to when login is required */ - @Value("#{'${frontend.loginUrl:/login/hbp}'}") + @Value("#{'${frontend.loginUrl}'}") private String loginUrl; - /** - * Absolute URL to redirect to when logout is required - */ - @Value("#{'${hbp.client.logoutUri}'}") - private String logoutUri; - /** * Absolute URL to redirect to after successful login */ - @Value("#{'${frontend.redirectAfterLoginUrl:http://frontend/home}'}") + @Value("#{'${frontend.redirectAfterLoginUrl}'}") private String frontendRedirectAfterLogin; /** - * Absolute URL to redirect to after logout has occurred + * Absolute URL to redirect to after successful logout */ - @Value("#{'${frontend.redirectAfterLogoutUrl:/login/hbp}'}") + @Value("#{'${frontend.redirectAfterLogoutUrl}'}") private String redirectAfterLogoutUrl; - /** - * URL to revoke auth token - */ - @Value("#{'${hbp.resource.revokeTokenUri:https://services.humanbrainproject.eu/oidc/revoke}'}") - private String revokeTokenURI; + public boolean getAuthenticationEnabled() { + return authenticationEnabled; + } + + public String getFrontendRedirectAfterLogin() { + return frontendRedirectAfterLogin; + } + + @Bean + public KeycloakConfigResolver KeycloakConfigResolver() { + return new KeycloakSpringBootConfigResolver(); + } + + @Override + protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { + return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); + } + + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) { + SimpleAuthorityMapper grantedAuthorityMapper = new SimpleAuthorityMapper(); + grantedAuthorityMapper.setPrefix("ROLE_"); + grantedAuthorityMapper.setConvertToUpperCase(true); + + KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider(); + keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(grantedAuthorityMapper); + auth.authenticationProvider(keycloakAuthenticationProvider); + } @Override protected void configure(HttpSecurity http) throws Exception { - disableCertificateValidation(); + disableCertificateValidation(); // TODO needed? // @formatter:off http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class); - if (authentication) { - http.antMatcher("/**") - .authorizeRequests() + if (authenticationEnabled) { + http.authorizeRequests() .antMatchers( - "/", "/login/**", "/health/**", "/info/**", "/metrics/**", "/trace/**", "/frontend/**", "/webjars/**", "/v2/api-docs", "/swagger-ui.html", "/swagger-resources/**" - ) - .permitAll() + "/", "/login/**", "/health/**", "/info/**", "/metrics/**", + "/trace/**", "/frontend/**", "/webjars/**", "/v2/api-docs", + "/swagger-ui.html", "/swagger-resources/**" + ).permitAll() .antMatchers("/galaxy*", "/galaxy/*").hasRole("Data Manager") - //.anyRequest().authenticated() .anyRequest().hasRole("Researcher") .and().exceptionHandling().authenticationEntryPoint(new CustomLoginUrlAuthenticationEntryPoint(loginUrl)) .accessDeniedHandler(new CustomAccessDeniedHandler()) - .and().logout().addLogoutHandler(authLogoutHandler()).logoutSuccessUrl(redirectAfterLogoutUrl) + // TODO .and().logout().addLogoutHandler(authLogoutHandler()).logoutSuccessUrl(redirectAfterLogoutUrl) .and().logout().permitAll() .and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository()) .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class) - .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class); + ; // TODO ?? .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class); } else { http.antMatcher("/**") .authorizeRequests() - .antMatchers("/**").permitAll().and().csrf().disable(); + .antMatchers("/**").permitAll() + .and().csrf().disable(); } } - private Filter ssoFilter() { - OAuth2ClientAuthenticationProcessingFilter hbpFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/hbp"); - OAuth2RestTemplate hbpTemplate = new OAuth2RestTemplate(hbp(), oauth2ClientContext); - hbpFilter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(frontendRedirectAfterLogin)); - hbpFilter.setRestTemplate(hbpTemplate); - hbpFilter.setTokenServices(new UserInfoTokenServices(hbpResource().getUserInfoUri(), hbp().getClientId())); - return hbpFilter; - } - - @Bean - public FilterRegistrationBean oauth2ClientFilterRegistration( - OAuth2ClientContextFilter filter) { - FilterRegistrationBean registration = new FilterRegistrationBean(); - registration.setFilter(filter); - registration.setOrder(-100); - return registration; - } - - @Bean(name = "hbp") - @ConfigurationProperties("hbp.client") - public BaseOAuth2ProtectedResourceDetails hbp() { - return new AuthorizationCodeResourceDetails(); - } +// private Filter ssoFilter() { +// OAuth2ClientAuthenticationProcessingFilter hbpFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/hbp"); +// OAuth2RestTemplate hbpTemplate = new OAuth2RestTemplate(hbp(), oauth2ClientContext); +// hbpFilter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(frontendRedirectAfterLogin)); +// hbpFilter.setRestTemplate(hbpTemplate); +// hbpFilter.setTokenServices(new UserInfoTokenServices(hbpResource().getUserInfoUri(), hbp().getClientId())); +// return hbpFilter; +// } + +// @Bean +// public FilterRegistrationBean oauth2ClientFilterRegistration( +// OAuth2ClientContextFilter filter) { +// FilterRegistrationBean registration = new FilterRegistrationBean(); +// registration.setFilter(filter); +// registration.setOrder(-100); +// return registration; +// } + +// @Bean(name = "hbp") +// @ConfigurationProperties("hbp.client") +// public BaseOAuth2ProtectedResourceDetails hbp() { +// return new AuthorizationCodeResourceDetails(); +// } +// +// @Bean(name = "hbpResource") +// @ConfigurationProperties("hbp.resource") +// public ResourceServerProperties hbpResource() { +// return new ResourceServerProperties(); +// } - @Bean(name = "hbpResource") - @ConfigurationProperties("hbp.resource") - public ResourceServerProperties hbpResource() { - return new ResourceServerProperties(); - } - - public boolean isAuthentication() { - return authentication; - } - - public String getFrontendRedirectAfterLogin() { - return frontendRedirectAfterLogin; - } private Filter csrfHeaderFilter() { return new OncePerRequestFilter() { @@ -199,107 +190,70 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { return repository; } - private class CustomLogoutHandler implements LogoutHandler { - @Override - public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) { - // Hackish way of accessing to this information... - final UserInfo userInfo = (UserInfo) httpServletRequest.getSession().getAttribute("userInfo"); - if (userInfo != null) { - userInfo.setFakeAuth(false); - } - - if (oauth2ClientContext == null || oauth2ClientContext.getAccessToken() == null) { - return; - } - - String idToken = oauth2ClientContext.getAccessToken().getAdditionalInformation().get("id_token").toString(); - - StringBuilder query = new StringBuilder(); - query.append("{"); - query.append("\"token\":"); - query.append("\"").append(idToken).append("\""); - query.append("}"); - - try { - int responseCode = HTTPUtil.sendPost(revokeTokenURI, query.toString(), new StringBuilder()); - if (responseCode != 200) { - LOGGER.warn("Cannot send request to OIDC server for revocation ! "); - } else { - LOGGER.info("Should be logged out"); - } - } catch (IOException e) { - LOGGER.warn("Cannot notify logout to OIDC server !"); - LOGGER.trace("Cannot notify logout", e); - } - - } - } - - @Bean - public AuthoritiesExtractor keycloakAuthoritiesExtractor() { - return new KeycloakAuthoritiesExtractor(); - } - - - public class KeycloakAuthoritiesExtractor - implements AuthoritiesExtractor { - - @Override - public List<GrantedAuthority> extractAuthorities - (Map<String, Object> map) { - return AuthorityUtils - .commaSeparatedStringToAuthorityList(asAuthorities(map)); - } - - private String asAuthorities(Map<String, Object> map) { - List<String> authorities = new ArrayList<>(); -// authorities.add("BAELDUNG_USER"); - List<LinkedHashMap<String, String>> authz; - authz = (List<LinkedHashMap<String, String>>) map.get("authorities"); - for (LinkedHashMap<String, String> entry : authz) { - authorities.add(entry.get("authority")); - } - return String.join(",", authorities); - } - } - - - private LogoutHandler authLogoutHandler() { - return (request, response, authentication) -> { - logout(); - }; - } - - - public void logout() { - // POSTã™ã‚‹ãƒªã‚¯ã‚¨ã‚¹ãƒˆãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã‚’ä½œæˆ - RestTemplate restTemplate = new RestTemplate(); - MultiValueMap<String, String> formParams = new LinkedMultiValueMap<>(); - formParams.add("client_id", hbp().getClientId()); - formParams.add("client_secret", hbp().getClientSecret()); - formParams.add("refresh_token", this.oauth2ClientContext.getAccessToken().getRefreshToken().getValue()); - // ãƒªã‚¯ã‚¨ã‚¹ãƒˆãƒ˜ãƒƒãƒ€ãƒ¼ã‚’ä½œæˆ - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE); - // ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’ä½œæˆ - RequestEntity<MultiValueMap<String, String>> requestEntity = - new RequestEntity<>(formParams, httpHeaders, HttpMethod.POST, - URI.create(logoutUri)); - // POSTリクエストé€ä¿¡ï¼ˆãƒã‚°ã‚¢ã‚¦ãƒˆå®Ÿè¡Œï¼‰ - - ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity, String.class); - } - - @Value("#{'${services.keycloak.keycloakUrl}'}") +// @Bean +// public AuthoritiesExtractor keycloakAuthoritiesExtractor() { +// return new KeycloakAuthoritiesExtractor(); +// } +// +// +// public class KeycloakAuthoritiesExtractor +// implements AuthoritiesExtractor { +// +// @Override +// public List<GrantedAuthority> extractAuthorities +// (Map<String, Object> map) { +// return AuthorityUtils +// .commaSeparatedStringToAuthorityList(asAuthorities(map)); +// } +// +// private String asAuthorities(Map<String, Object> map) { +// List<String> authorities = new ArrayList<>(); +//// authorities.add("BAELDUNG_USER"); +// List<LinkedHashMap<String, String>> authz; +// authz = (List<LinkedHashMap<String, String>>) map.get("authorities"); +// for (LinkedHashMap<String, String> entry : authz) { +// authorities.add(entry.get("authority")); +// } +// return String.join(",", authorities); +// } +// } + + +// private LogoutHandler authLogoutHandler() { +// return (request, response, authentication) -> { +// logout(); +// }; +// } + + +// public void logout() { +// // TODO Try removing +// +// RestTemplate restTemplate = new RestTemplate(); +// MultiValueMap<String, String> formParams = new LinkedMultiValueMap<>(); +// formParams.add("client_id", hbp().getClientId()); +// formParams.add("client_secret", hbp().getClientSecret()); +// formParams.add("refresh_token", this.oauth2ClientContext.getAccessToken().getRefreshToken().getValue()); +// +// HttpHeaders httpHeaders = new HttpHeaders(); +// httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE); +// RequestEntity<MultiValueMap<String, String>> requestEntity = +// new RequestEntity<>(formParams, httpHeaders, HttpMethod.POST, +// URI.create(logoutUrl)); +// restTemplate.exchange(requestEntity, String.class); +// } + +// TODO needed? + @Value("#{'${keycloak.auth-server-url}'}") private String keycloakUrl; - // static { - // disableCertificateValidation(); - // } - public void disableCertificateValidation() { + + //TODO Refactor logging + LOGGER.info("disabling certificate validation host : " + keycloakUrl); + // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { @@ -316,18 +270,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { // Ignore differences between given hostname and certificate hostname - HostnameVerifier hv = new HostnameVerifier() { - public boolean verify(String hostname, SSLSession session) { - - // System.out.println("Warning: URL Host: " + hostname + " vs. " - // + session.getPeerHost()); - if (hostname.equals(keycloakUrl) && session.getPeerHost().equals(keycloakUrl)) { - return true; - } else { - return false; - } - } - }; + HostnameVerifier hv = + (hostname, session) -> hostname.equals(keycloakUrl) && session.getPeerHost().equals(keycloakUrl); // Install the all-trusting trust manager try { @@ -336,6 +280,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier(hv); } catch (Exception e) { + // TODO add log message } } diff --git a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java index ff8d70a80..716f0da00 100644 --- a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java +++ b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java @@ -55,8 +55,8 @@ public class SecurityApi { //LOGGER.trace("Cannot read user json", e); } - if (!securityConfiguration.isAuthentication()) { - if (userInfo.isFakeAuth()) { + if (!securityConfiguration.getAuthenticationEnabled()) { + if (userInfo.getFakeAuth()) { response.setStatus(401); } String principalJson = "{\"principal\": \"anonymous\", \"name\": \"anonymous\", \"userAuthentication\": {" @@ -85,7 +85,7 @@ public class SecurityApi { @ConditionalOnExpression("${hbp.authentication.enabled:0}") public void noLogin(HttpServletResponse httpServletResponse) throws IOException { userInfo.setFakeAuth(true); - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /user/login/hbp", "Unathorized login."); + Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /user/login/hbp", "Unauthorized login."); httpServletResponse.sendRedirect(securityConfiguration.getFrontendRedirectAfterLogin()); } @@ -104,7 +104,6 @@ public class SecurityApi { * * @return Return a @{@link ResponseEntity} with the token. */ - @RequestMapping(path = "/galaxy", method = RequestMethod.GET, produces = "application/json") @PreAuthorize("hasRole('Data Manager')") @ResponseStatus(value = HttpStatus.OK) diff --git a/src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java b/src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java index 1e35ac831..ccf2a7fd7 100644 --- a/src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java +++ b/src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java @@ -19,7 +19,6 @@ import java.util.regex.Pattern; @Table(name = "`user`") @ApiModel @JsonInclude(JsonInclude.Include.NON_NULL) -@JsonIgnoreProperties(value = {"appsVotes"}) public class UserDAO { @Id @@ -49,26 +48,33 @@ public class UserDAO { */ public UserDAO(String userInfo) { - Pattern p; - Matcher m; - - p = Pattern.compile("preferred_username=([\\w- ]+)"); - m = p.matcher(userInfo); - if (m.find()) { - this.username = m.group(1); - } - - p = Pattern.compile("name=([\\w ]+)"); - m = p.matcher(userInfo); - if (m.find()) { - this.fullname = m.group(1); - } - - p = Pattern.compile("email=([\\w.]+@[\\w.]+)"); - m = p.matcher(userInfo); - if (m.find()) { - this.email = m.group(1); - } + + // TODO fix + this.username = "test"; + this.fullname = "test"; + this.email = "test"; + + // +// Pattern p; +// Matcher m; +// +// p = Pattern.compile("preferred_username=([\\w- ]+)"); +// m = p.matcher(userInfo); +// if (m.find()) { +// this.username = m.group(1); +// } +// +// p = Pattern.compile("name=([\\w ]+)"); +// m = p.matcher(userInfo); +// if (m.find()) { +// this.fullname = m.group(1); +// } +// +// p = Pattern.compile("email=([\\w.]+@[\\w.]+)"); +// m = p.matcher(userInfo); +// if (m.find()) { +// this.email = m.group(1); +// } } diff --git a/src/main/java/eu/hbp/mip/model/UserInfo.java b/src/main/java/eu/hbp/mip/model/UserInfo.java index cb61cfcea..4f58aaa41 100644 --- a/src/main/java/eu/hbp/mip/model/UserInfo.java +++ b/src/main/java/eu/hbp/mip/model/UserInfo.java @@ -6,9 +6,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.stereotype.Component; import javax.inject.Named; @@ -24,7 +21,7 @@ public class UserInfo { /** * Enable HBP collab authentication (1) or disable it (0). Default is 1 */ - @Value("#{'${hbp.authentication.enabled:1}'}") + @Value("#{'${hbp.authentication.enabled}'}") private boolean authentication; private UserDAO user; @@ -55,7 +52,7 @@ public class UserInfo { user.setFullname("anonymous"); user.setEmail("anonymous@anonymous.com"); } else { - user = new UserDAO(getUserInfos()); + user = new UserDAO(getUserInformation()); } UserDAO foundUser = userRepository.findByUsername(user.getUsername()); if (foundUser != null) { @@ -67,7 +64,7 @@ public class UserInfo { return user; } - public boolean isFakeAuth() { + public boolean getFakeAuth() { return fakeAuth; } @@ -75,10 +72,13 @@ public class UserInfo { this.fakeAuth = fakeAuth; } - private String getUserInfos() { - OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication(); - Authentication userAuthentication = oAuth2Authentication.getUserAuthentication(); - return userAuthentication.getDetails().toString(); + private String getUserInformation() { + // TODO + return ""; + // get details from keycloak configuration +// OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication(); +// Authentication userAuthentication = oAuth2Authentication.getUserAuthentication(); +// return userAuthentication.getDetails().toString(); } } -- GitLab From 4afa62b65f816d6a92bbc30ad570363fe6f43430 Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Mon, 9 Nov 2020 11:07:49 +0200 Subject: [PATCH 16/52] Fixed log level env variables for portal-backend. --- docker/config/application.tmpl | 1 + .../java/eu/hbp/mip/configuration/SecurityConfiguration.java | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index 84ef19c5e..b78a41f59 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -71,4 +71,5 @@ keycloak: resource: {{ .Env.CLIENT_ID }} credentials: secret: {{ .Env.CLIENT_SECRET }} + principal-attribute: "preferred_username" # logoutUrl: {{ .Env.LOGOUT_URL }} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index 68a1ca4a1..323d974a2 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -122,8 +122,9 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter .accessDeniedHandler(new CustomAccessDeniedHandler()) // TODO .and().logout().addLogoutHandler(authLogoutHandler()).logoutSuccessUrl(redirectAfterLogoutUrl) .and().logout().permitAll() - .and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository()) - .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class) + .and().csrf().disable() // TODO Remove + //.and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository()) + //.and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class) ; // TODO ?? .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class); } else { http.antMatcher("/**") -- GitLab From 5e34a2363bdc24016d308c96359a5855f0bc4097 Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Mon, 9 Nov 2020 11:21:19 +0200 Subject: [PATCH 17/52] Configuring keycloak security. --- build.sh | 2 +- docker/config/application.tmpl | 37 ++-- pom.xml | 16 +- .../configuration/SecurityConfiguration.java | 166 +++++++++++------- .../SecurityUtils}/CORSFilter.java | 12 +- .../CustomAccessDeniedHandler.java | 5 +- ...ustomLoginUrlAuthenticationEntryPoint.java | 5 +- .../hbp/mip/controllers/PathologiesApi.java | 2 +- .../eu/hbp/mip/controllers/SecurityApi.java | 16 +- .../java/eu/hbp/mip/model/DAOs/UserDAO.java | 5 - src/main/java/eu/hbp/mip/model/UserInfo.java | 3 +- .../hbp/mip/services/ExperimentService.java | 2 +- src/main/resources/application.conf | 20 --- src/main/resources/kamon.conf | 75 -------- 14 files changed, 153 insertions(+), 213 deletions(-) rename src/main/java/eu/hbp/mip/{utils => configuration/SecurityUtils}/CORSFilter.java (79%) rename src/main/java/eu/hbp/mip/{utils => configuration/SecurityUtils}/CustomAccessDeniedHandler.java (92%) rename src/main/java/eu/hbp/mip/{utils => configuration/SecurityUtils}/CustomLoginUrlAuthenticationEntryPoint.java (87%) delete mode 100644 src/main/resources/application.conf delete mode 100644 src/main/resources/kamon.conf diff --git a/build.sh b/build.sh index 6a18d98ec..458b5c085 100755 --- a/build.sh +++ b/build.sh @@ -26,7 +26,7 @@ else DOCKER="sudo docker" fi -IMAGE="kfilippopolitis/portal-backend" +IMAGE="thanasulas/portal-backend" VCS_REF=$(git describe --tags --dirty) VERSION=$(git describe --tags --dirty) diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index b78a41f59..3b9311454 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -3,7 +3,7 @@ # See http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html spring: portal-datasource: - url: {{ default .Env.PORTAL_DB_URL "jdbc:postgresql://172.22.0.1:5432/portal" }} + url: {{ default .Env.PORTAL_DB_URL "jdbc:postgresql://88.197.53.106:5432/portal" }} schema: {{ default .Env.PORTAL_DB_SCHEMA "public" }} username: {{ default .Env.PORTAL_DB_USER "postgres" }} password: {{ .Env.PORTAL_DB_PASSWORD }} @@ -22,17 +22,19 @@ spring: frontend: loginUrl: {{ default .Env.FRONTEND_LOGIN_URL "http://frontend/services/login/hbp" }} redirectAfterLoginUrl: {{ default .Env.FRONTEND_AFTER_LOGIN_URL "http://frontend/" }} - redirectAfterLogoutUrl: {{ default .Env.FRONTEND_AFTER_LOGOUT_URL (default .Env.LOGIN_URI "http://frontend/services/login/hbp") }} + redirectAfterLogoutUrl: {{ default .Env.FRONTEND_AFTER_LOGOUT_URL "http://frontend/services/login/hbp" }} logging: level: root: {{ default .Env.LOG_LEVEL_FRAMEWORK "ERROR" }} + org: {{ default .Env.LOG_LEVEL_FRAMEWORK "ERROR" }} eu: hbp: {{ default .Env.LOG_LEVEL "INFO" }} # EMBEDDED SERVER CONFIGURATION server: - contextPath: {{ default .Env.CONTEXT_PATH "/services" }} + servlet: + contextPath: {{ default .Env.CONTEXT_PATH "/services" }} port: 8080 use-forward-headers: true session: @@ -59,17 +61,24 @@ services: galaxyUsername: {{ default .Env.GALAXY_USERNAME "admin" }} galaxyPassword: {{ default .Env.GALAXY_PASSWORD "password" }} -# HBP Authentication -hbp: - authentication: - enabled: {{ default .Env.AUTHENTICATION "1" }} +# Authentication +authentication: + enabled: {{ default .Env.AUTHENTICATION "1" }} # Keycloak keycloak: - auth-server-url: {{ .Env.KEYCLOAK_URL }} - realm: {{ .Env.KEYCLOAK_REALM }} - resource: {{ .Env.CLIENT_ID }} - credentials: - secret: {{ .Env.CLIENT_SECRET }} - principal-attribute: "preferred_username" - # logoutUrl: {{ .Env.LOGOUT_URL }} \ No newline at end of file + enabled: true + auth-server-url: {{ .Env.KEYCLOAK_AUTH_URL }} + realm: {{ .Env.KEYCLOAK_REALM }} + resource: {{ .Env.CLIENT_ID }} + enable-basic-auth: true + credentials: + secret: {{ .Env.CLIENT_SECRET }} + principal-attribute: "preferred_username" +# cors: true +# cors-max-age: 3600 +# cors-allowed-methods: "GET, POST, PUT, PATCH, OPTIONS, DELETE" +# cors-allowed-headers: "*" +# cors-exposed-headers: "*" + + # logoutUrl: {{ .Env.LOGOUT_URL }} diff --git a/pom.xml b/pom.xml index a856ee073..e5d87b28f 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <keycloak-spring-boot-starter.version>11.0.2</keycloak-spring-boot-starter.version> + <keycloak-spring.version>11.0.2</keycloak-spring.version> <springfox-swagger.version>2.5.0</springfox-swagger.version> <spring-context.version>5.2.10.RELEASE</spring-context.version> <springfox-boot-starter.version>3.0.0</springfox-boot-starter.version> @@ -44,6 +44,7 @@ <flyway-core.version>4.2.0</flyway-core.version> <hibernate-jpa-2.1-api.version>1.0.0.Final</hibernate-jpa-2.1-api.version> <hibernate.version>5.4.10.Final</hibernate.version> + <hibernate-validator.version>6.1.0.Final</hibernate-validator.version> <aspectjweaver.version>1.8.9</aspectjweaver.version> <javax-inject.version>1</javax-inject.version> <protobuf-java.version>3.1.0</protobuf-java.version> @@ -84,9 +85,13 @@ <dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-spring-boot-starter</artifactId> - <version>${keycloak-spring-boot-starter.version}</version> + <version>${keycloak-spring.version}</version> + </dependency> + <dependency> + <groupId>org.keycloak</groupId> + <artifactId>keycloak-spring-security-adapter</artifactId> + <version>${keycloak-spring.version}</version> </dependency> - <!-- <dependency>--> <!-- <groupId>org.springframework.security</groupId>--> <!-- <artifactId>spring-security-oauth2-resource-server</artifactId>--> @@ -140,6 +145,11 @@ <artifactId>hibernate-core</artifactId> <version>${hibernate.version}</version> </dependency> + <dependency> + <groupId>org.hibernate</groupId> + <artifactId>hibernate-validator</artifactId> + <version>${hibernate-validator.version}</version> + </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index 323d974a2..d2a0dd739 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -1,35 +1,40 @@ package eu.hbp.mip.configuration; -import eu.hbp.mip.utils.CORSFilter; -import eu.hbp.mip.utils.CustomAccessDeniedHandler; -import eu.hbp.mip.utils.CustomLoginUrlAuthenticationEntryPoint; +import eu.hbp.mip.configuration.SecurityUtils.CORSFilter; +import eu.hbp.mip.configuration.SecurityUtils.CustomAccessDeniedHandler; +import eu.hbp.mip.configuration.SecurityUtils.CustomLoginUrlAuthenticationEntryPoint; import org.keycloak.adapters.KeycloakConfigResolver; import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; import org.keycloak.adapters.springsecurity.KeycloakConfiguration; import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; +import org.keycloak.adapters.springsecurity.authentication.KeycloakLogoutHandler; import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; +import org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter; +import org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.web.access.channel.ChannelProcessingFilter; -import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy; import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; import org.springframework.security.web.csrf.CsrfFilter; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.csrf.CsrfTokenRepository; import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.WebUtils; import javax.net.ssl.*; -import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.Cookie; @@ -52,7 +57,7 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter /** * Enable HBP collab authentication (1) or disable it (0). Default is 1 */ - @Value("#{'${hbp.authentication.enabled}'}") + @Value("#{'${authentication.enabled}'}") private boolean authenticationEnabled; /** @@ -91,11 +96,10 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); } - @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) { SimpleAuthorityMapper grantedAuthorityMapper = new SimpleAuthorityMapper(); - grantedAuthorityMapper.setPrefix("ROLE_"); + //grantedAuthorityMapper.setPrefix("ROLE_"); grantedAuthorityMapper.setConvertToUpperCase(true); KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider(); @@ -103,29 +107,41 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter auth.authenticationProvider(keycloakAuthenticationProvider); } + @Override + protected KeycloakLogoutHandler keycloakLogoutHandler() throws Exception { + return super.keycloakLogoutHandler(); + } + @Override protected void configure(HttpSecurity http) throws Exception { - disableCertificateValidation(); // TODO needed? - // @formatter:off + super.configure(http); + //disableCertificateValidation(); // TODO needed? + + // TODO Check if needed. + // Check if it works when removing keycloak cors from app properties. http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class); if (authenticationEnabled) { - http.authorizeRequests() - .antMatchers( - "/", "/login/**", "/health/**", "/info/**", "/metrics/**", - "/trace/**", "/frontend/**", "/webjars/**", "/v2/api-docs", - "/swagger-ui.html", "/swagger-resources/**" - ).permitAll() - .antMatchers("/galaxy*", "/galaxy/*").hasRole("Data Manager") - .anyRequest().hasRole("Researcher") - .and().exceptionHandling().authenticationEntryPoint(new CustomLoginUrlAuthenticationEntryPoint(loginUrl)) - .accessDeniedHandler(new CustomAccessDeniedHandler()) - // TODO .and().logout().addLogoutHandler(authLogoutHandler()).logoutSuccessUrl(redirectAfterLogoutUrl) - .and().logout().permitAll() - .and().csrf().disable() // TODO Remove - //.and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository()) - //.and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class) - ; // TODO ?? .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class); + http.antMatcher("/**") + .authorizeRequests() + .antMatchers("/**").permitAll() +// .antMatchers( +// "/login**", "/health/**", "/info/**", "/metrics/**", +// "/trace/**", "/frontend/**", "/webjars/**", "/v2/api-docs", +// "/swagger-ui.html", "/swagger-resources/**" +// ).permitAll() +// .antMatchers("/galaxy*", "/galaxy/*").hasRole("DATA MANAGER") +// .anyRequest().hasRole("RESEARCHER") + // .and().exceptionHandling().authenticationEntryPoint(new CustomLoginUrlAuthenticationEntryPoint(loginUrl)) + // .accessDeniedHandler(new CustomAccessDeniedHandler()) + .and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository()) + .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class).csrf(); +// .and().logout().logoutSuccessUrl("/logout"); +// +// +// // TODO .and().logout().addLogoutHandler(authLogoutHandler()).logoutSuccessUrl(redirectAfterLogoutUrl) +// .and().logout().permitAll() + // TODO ?? .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class); } else { http.antMatcher("/**") .authorizeRequests() @@ -134,6 +150,22 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter } } + +// @Bean +// public FilterRegistrationBean corsFilter() { +// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); +// CorsConfiguration config = new CorsConfiguration(); +// config.setAllowCredentials(true); +// config.addAllowedOrigin("*"); +// config.addAllowedHeader("*"); +// config.addAllowedMethod("*"); +// source.registerCorsConfiguration("/**", config); +// +// FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); +// bean.setOrder(-100); +// return bean; +// } + // private Filter ssoFilter() { // OAuth2ClientAuthenticationProcessingFilter hbpFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/hbp"); // OAuth2RestTemplate hbpTemplate = new OAuth2RestTemplate(hbp(), oauth2ClientContext); @@ -165,7 +197,7 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter // } - private Filter csrfHeaderFilter() { + private OncePerRequestFilter csrfHeaderFilter() { return new OncePerRequestFilter() { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, @@ -245,45 +277,45 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter // restTemplate.exchange(requestEntity, String.class); // } -// TODO needed? - @Value("#{'${keycloak.auth-server-url}'}") - private String keycloakUrl; - - public void disableCertificateValidation() { - - //TODO Refactor logging - - LOGGER.info("disabling certificate validation host : " + keycloakUrl); - - // Create a trust manager that does not validate certificate chains - TrustManager[] trustAllCerts = new TrustManager[]{ - new X509TrustManager() { - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - - public void checkClientTrusted(X509Certificate[] certs, String authType) { - } - - public void checkServerTrusted(X509Certificate[] certs, String authType) { - } - }}; - - - // Ignore differences between given hostname and certificate hostname - HostnameVerifier hv = - (hostname, session) -> hostname.equals(keycloakUrl) && session.getPeerHost().equals(keycloakUrl); - - // Install the all-trusting trust manager - try { - SSLContext sc = SSLContext.getInstance("SSL"); - sc.init(null, trustAllCerts, new SecureRandom()); - HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); - HttpsURLConnection.setDefaultHostnameVerifier(hv); - } catch (Exception e) { - // TODO add log message - } - - } + // TODO needed? +// @Value("#{'${keycloak.auth-server-url}'}") +// private String keycloakUrl; +// +// public void disableCertificateValidation() { +// +// //TODO Refactor logging +// +// LOGGER.info("disabling certificate validation host : " + keycloakUrl); +// +// // Create a trust manager that does not validate certificate chains +// TrustManager[] trustAllCerts = new TrustManager[]{ +// new X509TrustManager() { +// public X509Certificate[] getAcceptedIssuers() { +// return new X509Certificate[0]; +// } +// +// public void checkClientTrusted(X509Certificate[] certs, String authType) { +// } +// +// public void checkServerTrusted(X509Certificate[] certs, String authType) { +// } +// }}; +// +// +// // Ignore differences between given hostname and certificate hostname +// HostnameVerifier hv = +// (hostname, session) -> hostname.equals(keycloakUrl) && session.getPeerHost().equals(keycloakUrl); +// +// // Install the all-trusting trust manager +// try { +// SSLContext sc = SSLContext.getInstance("SSL"); +// sc.init(null, trustAllCerts, new SecureRandom()); +// HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); +// HttpsURLConnection.setDefaultHostnameVerifier(hv); +// } catch (Exception e) { +// // TODO add log message +// } +// +// } } diff --git a/src/main/java/eu/hbp/mip/utils/CORSFilter.java b/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CORSFilter.java similarity index 79% rename from src/main/java/eu/hbp/mip/utils/CORSFilter.java rename to src/main/java/eu/hbp/mip/configuration/SecurityUtils/CORSFilter.java index 7ed1ecf38..2a259ea22 100644 --- a/src/main/java/eu/hbp/mip/utils/CORSFilter.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CORSFilter.java @@ -1,4 +1,4 @@ -package eu.hbp.mip.utils; +package eu.hbp.mip.configuration.SecurityUtils; import javax.servlet.*; import javax.servlet.http.HttpServletResponse; @@ -19,14 +19,4 @@ public class CORSFilter implements Filter { response.setHeader("Access-Control-Request-Headers", "*"); chain.doFilter(req, res); } - - @Override - public void init(FilterConfig filterConfig) { - /* Nothing to do */ - } - - @Override - public void destroy() { - /* Nothing to do */ - } } diff --git a/src/main/java/eu/hbp/mip/utils/CustomAccessDeniedHandler.java b/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CustomAccessDeniedHandler.java similarity index 92% rename from src/main/java/eu/hbp/mip/utils/CustomAccessDeniedHandler.java rename to src/main/java/eu/hbp/mip/configuration/SecurityUtils/CustomAccessDeniedHandler.java index 783e4a3ff..552abd29f 100644 --- a/src/main/java/eu/hbp/mip/utils/CustomAccessDeniedHandler.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CustomAccessDeniedHandler.java @@ -1,11 +1,10 @@ -package eu.hbp.mip.utils; +package eu.hbp.mip.configuration.SecurityUtils; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; -import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @@ -14,7 +13,7 @@ import java.sql.Timestamp; public class CustomAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, - AccessDeniedException accessDeniedException) throws IOException, ServletException { + AccessDeniedException accessDeniedException) throws IOException { response.setContentType("application/json;charset=UTF-8"); response.setStatus(403); try { diff --git a/src/main/java/eu/hbp/mip/utils/CustomLoginUrlAuthenticationEntryPoint.java b/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CustomLoginUrlAuthenticationEntryPoint.java similarity index 87% rename from src/main/java/eu/hbp/mip/utils/CustomLoginUrlAuthenticationEntryPoint.java rename to src/main/java/eu/hbp/mip/configuration/SecurityUtils/CustomLoginUrlAuthenticationEntryPoint.java index 45870c777..068ca591b 100644 --- a/src/main/java/eu/hbp/mip/utils/CustomLoginUrlAuthenticationEntryPoint.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CustomLoginUrlAuthenticationEntryPoint.java @@ -1,9 +1,8 @@ -package eu.hbp.mip.utils; +package eu.hbp.mip.configuration.SecurityUtils; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; -import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @@ -15,7 +14,7 @@ public class CustomLoginUrlAuthenticationEntryPoint extends LoginUrlAuthenticati } @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { response.sendError(HttpServletResponse.SC_UNAUTHORIZED); } } diff --git a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java index 9560cc6dd..4b37d2c5d 100644 --- a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java +++ b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java @@ -38,7 +38,7 @@ public class PathologiesApi { private UserInfo userInfo; // Enable HBP collab authentication (1) or disable it (0). Default is 1 - @Value("#{'${hbp.authentication.enabled:1}'}") + @Value("#{'${authentication.enabled}'}") private boolean authenticationIsEnabled; @Autowired diff --git a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java index 716f0da00..9d2b66492 100644 --- a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java +++ b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java @@ -81,14 +81,14 @@ public class SecurityApi { return new ResponseEntity<>(HttpStatus.NO_CONTENT); } - @RequestMapping(path = "/login/hbp", method = RequestMethod.GET) - @ConditionalOnExpression("${hbp.authentication.enabled:0}") - public void noLogin(HttpServletResponse httpServletResponse) throws IOException { - userInfo.setFakeAuth(true); - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /user/login/hbp", "Unauthorized login."); - - httpServletResponse.sendRedirect(securityConfiguration.getFrontendRedirectAfterLogin()); - } +// @RequestMapping(path = "/login/hbp", method = RequestMethod.GET) +// @ConditionalOnExpression("${authentication.enabled:0}") +// public void noLogin(HttpServletResponse httpServletResponse) throws IOException { +// userInfo.setFakeAuth(true); +// Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /user/login/hbp", "Unauthorized login."); +// +// httpServletResponse.sendRedirect(securityConfiguration.getFrontendRedirectAfterLogin()); +// } @Value("#{'${services.galaxy.galaxyUsername:admin}'}") private String galaxyUsername; diff --git a/src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java b/src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java index ccf2a7fd7..0d0ba246d 100644 --- a/src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java +++ b/src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java @@ -4,7 +4,6 @@ package eu.hbp.mip.model.DAOs; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.google.gson.annotations.Expose; import io.swagger.annotations.ApiModel; @@ -12,8 +11,6 @@ import io.swagger.annotations.ApiModel; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; -import java.util.regex.Matcher; -import java.util.regex.Pattern; @Entity @Table(name = "`user`") @@ -88,7 +85,6 @@ public class UserDAO { this.fullname = fullname; } - public String getUsername() { return username; } @@ -97,7 +93,6 @@ public class UserDAO { this.username = username; } - public String getEmail() { return email; } diff --git a/src/main/java/eu/hbp/mip/model/UserInfo.java b/src/main/java/eu/hbp/mip/model/UserInfo.java index 4f58aaa41..617b25a5f 100644 --- a/src/main/java/eu/hbp/mip/model/UserInfo.java +++ b/src/main/java/eu/hbp/mip/model/UserInfo.java @@ -21,7 +21,7 @@ public class UserInfo { /** * Enable HBP collab authentication (1) or disable it (0). Default is 1 */ - @Value("#{'${hbp.authentication.enabled}'}") + @Value("#{'${authentication.enabled}'}") private boolean authentication; private UserDAO user; @@ -54,6 +54,7 @@ public class UserInfo { } else { user = new UserDAO(getUserInformation()); } + UserDAO foundUser = userRepository.findByUsername(user.getUsername()); if (foundUser != null) { user.setAgreeNDA(foundUser.getAgreeNDA()); diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index a02af3ab6..fe1b4023c 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -55,7 +55,7 @@ public class ExperimentService { private String galaxyApiKey; // Enable HBP collab authentication (1) or disable it (0). Default is 1 - @Value("#{'${hbp.authentication.enabled:1}'}") + @Value("#{'${authentication.enabled:1}'}") private boolean authenticationIsEnabled; @Autowired diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf deleted file mode 100644 index e2abfd5ae..000000000 --- a/src/main/resources/application.conf +++ /dev/null @@ -1,20 +0,0 @@ -app { - # Name of the application - name = "Portal backend" - # Type of the application - type = "Spring" - clusterSystemName = ${clustering.cluster.name} -} - -clustering { - ip = "127.0.0.1" - ip = ${?CLUSTER_IP} - port = 4489 - port = ${?CLUSTER_PORT} -} - -akka { - cluster { - roles = ["portal"] - } -} diff --git a/src/main/resources/kamon.conf b/src/main/resources/kamon.conf deleted file mode 100644 index face394cb..000000000 --- a/src/main/resources/kamon.conf +++ /dev/null @@ -1,75 +0,0 @@ -kamon { - enabled = no - enabled = ${?KAMON_ENABLED} - - environment { - service = "portal-backend" - service = ${?KAMON_SERVICE_NAME} - } - - zipkin = { - enabled = no - enabled = ${?ZIPKIN_ENABLED} - host = "zipkin" - host = ${?ZIPKIN_IP} - port = 9411 - port = ${?ZIPKIN_PORT} - } - - prometheus = { - enabled = no - enabled = ${?PROMETHEUS_ENABLED} - host = "prometheus" - host = ${?PROMETHEUS_IP} - port = 9090 - port = ${?PROMETHEUS_PORT} - } - - util.filters { - "akka.tracked-actor" { - includes = ["**"] - } - - "akka.tracked-dispatcher" { - includes = ["**"] - } - - "akka.traced-actor" { - includes = ["**"] - excludes = [ - ${clustering.cluster.name}"/system/IO**", - ${clustering.cluster.name}"/user/Stream**", - ${clustering.cluster.name}"/system/transports**", - ${clustering.cluster.name}"/system/cluster**", - ${clustering.cluster.name}"/system/remote**", - ${clustering.cluster.name}"/system/endpointmanager/**", - ${clustering.cluster.name}"/system/sharding/UserActor/**"] - } - - } - - akka-http { - add-http-status-code-as-metric-tag = true - } - - akka { - ask-pattern-timeout-warning = lightweight - } - - trace { - join-remote-parents-with-same-span-id = yes - sampler = "always" - } - - system-metrics { - host { - enabled = no - enabled = ${?SIGAR_SYSTEM_METRICS} - } - jvm { - enabled = no - enabled = ${?JVM_SYSTEM_METRICS} - } - } - -} -- GitLab From 7a382421a40d1085c5ddad91615c323b7c81c6f2 Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Tue, 10 Nov 2020 08:54:12 -0800 Subject: [PATCH 18/52] Implemented exteption/error handling and added java docs on service layer. --- .../eu/hbp/mip/controllers/ExperimentApi.java | 32 +- .../hbp/mip/controllers/PathologiesApi.java | 3 +- .../eu/hbp/mip/model/DAOs/ExperimentDAO.java | 2 +- .../eu/hbp/mip/model/DTOs/ExperimentDTO.java | 7 +- .../hbp/mip/services/ExperimentService.java | 385 +++++++++++++----- .../services/ExperimentSpecifications.java | 7 +- .../mip/utils/ControllerExceptionHandler.java | 62 ++- .../utils/Exceptions/BadRequestException.java | 8 + .../ExperimentNotFoundException.java | 8 + .../utils/Exceptions/InternalServerError.java | 8 + .../hbp/mip/utils/Exceptions/NoContent.java | 8 + .../Exceptions/UnauthorizedException.java | 8 + .../mip/utils/ResourceNotFoundException.java | 10 - ...ure.sql => V7_0__NewDatabaseStructure.sql} | 0 14 files changed, 423 insertions(+), 125 deletions(-) create mode 100644 src/main/java/eu/hbp/mip/utils/Exceptions/BadRequestException.java create mode 100644 src/main/java/eu/hbp/mip/utils/Exceptions/ExperimentNotFoundException.java create mode 100644 src/main/java/eu/hbp/mip/utils/Exceptions/InternalServerError.java create mode 100644 src/main/java/eu/hbp/mip/utils/Exceptions/NoContent.java create mode 100644 src/main/java/eu/hbp/mip/utils/Exceptions/UnauthorizedException.java delete mode 100644 src/main/java/eu/hbp/mip/utils/ResourceNotFoundException.java rename src/main/resources/db/migration/{V7_0__NewExperimentStructure.sql => V7_0__NewDatabaseStructure.sql} (100%) diff --git a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java index 7cb949a4b..322f33f4f 100644 --- a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java +++ b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java @@ -3,14 +3,18 @@ package eu.hbp.mip.controllers; import eu.hbp.mip.model.DTOs.ExperimentDTO; import eu.hbp.mip.model.UserInfo; import eu.hbp.mip.services.ExperimentService; +import eu.hbp.mip.utils.JsonConverters; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; +import java.util.Map; + import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; /* @@ -39,32 +43,50 @@ public class ExperimentApi { @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "3") int size ) { - return experimentService.getExperiments(name, algorithm, shared, viewed, page, size, "(GET) /experiments"); + Map experiments = experimentService.getExperiments(name, algorithm, shared, viewed, page, size, "(GET) /experiments"); + return new ResponseEntity(experiments, HttpStatus.OK); } + @ApiOperation(value = "Get an experiment", response = ExperimentDTO.class) @RequestMapping(value = "/{uuid}", method = RequestMethod.GET) public ResponseEntity<String> getExperiment( @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - return experimentService.getExperiment(uuid, "(GET) /experiments/{uuid}"); + ExperimentDTO experimentDTO = experimentService.getExperiment(uuid, "(GET) /experiments/{uuid}"); + return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); } + @ApiOperation(value = "Create an experiment", response = ExperimentDTO.class) @RequestMapping(method = RequestMethod.POST) public ResponseEntity<String> createExperiment(Authentication authentication, @RequestBody ExperimentDTO experimentDTO) { - return experimentService.createExperiment(authentication, experimentDTO, "(POST) /experiments"); + experimentDTO = experimentService.createExperiment(authentication, experimentDTO, "(POST) /experiments"); + return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.CREATED); } + + @ApiOperation(value = "Create a transient experiment", response = ExperimentDTO.class) + @RequestMapping(value = "/{transient}",method = RequestMethod.POST) + public ResponseEntity<String> createTransientExperiment(Authentication authentication, @RequestBody ExperimentDTO experimentDTO) { + experimentDTO = experimentService.createTransientExperiment(authentication, experimentDTO, "(POST) /experiments/transient"); + + return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); + } + + @ApiOperation(value = "Update an experiment", response = ExperimentDTO.class) @RequestMapping(value = "/{uuid}", method = RequestMethod.PATCH) public ResponseEntity<String> updateExperiment(@RequestBody ExperimentDTO experimentDTO,@ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - return experimentService.updateExperiment(uuid, experimentDTO, "(PATCH) /experiments/{uuid}"); + experimentDTO = experimentService.updateExperiment(uuid, experimentDTO, "(PATCH) /experiments/{uuid}"); + return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); } + @ApiOperation(value = "Delete an experiment", response = boolean.class) @RequestMapping(value = "/{uuid}", method = RequestMethod.DELETE) public ResponseEntity<String> deleteExperiment( @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - return experimentService.deleteExperiment(uuid, "(DELETE) /experiments/{uuid}"); + experimentService.deleteExperiment(uuid, "(DELETE) /experiments/{uuid}"); + return new ResponseEntity<>(HttpStatus.OK); } } \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java index 4b37d2c5d..6c89e1b94 100644 --- a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java +++ b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java @@ -10,6 +10,7 @@ import eu.hbp.mip.model.DTOs.PathologyDTO; import eu.hbp.mip.model.UserInfo; import eu.hbp.mip.utils.ClaimUtils; import eu.hbp.mip.utils.CustomResourceLoader; +import eu.hbp.mip.utils.Exceptions.BadRequestException; import eu.hbp.mip.utils.InputStreamConverter; import eu.hbp.mip.utils.Logging; import io.swagger.annotations.Api; @@ -56,7 +57,7 @@ public class PathologiesApi { }.getType()); } catch (IOException e) { Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /pathologies", "Unable to load pathologies"); - return ResponseEntity.badRequest().body("The pathologies could not be loaded."); + throw new BadRequestException("The pathologies could not be loaded."); } // If authentication is disabled return everything diff --git a/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java b/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java index 30d6c87da..2ee8e0ba4 100644 --- a/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java +++ b/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java @@ -120,7 +120,7 @@ public class ExperimentDAO { experimentDTO.setName(this.name); experimentDTO.setResult(JsonConverters.convertJsonStringToObject(this.result, ExperimentDTO.ResultDTO.class)); experimentDTO.setShared(this.shared); - experimentDTO.setUuid(this.uuid.toString()); + experimentDTO.setUuid(this.uuid); experimentDTO.setViewed(this.viewed); return experimentDTO; } diff --git a/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java b/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java index e4b9d34a5..64ade455b 100644 --- a/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java +++ b/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java @@ -4,10 +4,11 @@ import eu.hbp.mip.model.DAOs.ExperimentDAO; import java.util.Date; import java.util.List; +import java.util.UUID; public class ExperimentDTO { - private String uuid; + private UUID uuid; private String name; private String createdBy; private Date created; @@ -39,11 +40,11 @@ public class ExperimentDTO { this.algorithm = algorithmDetails.getName(); } - public String getUuid() { + public UUID getUuid() { return uuid; } - public void setUuid(String uuid) { + public void setUuid(UUID uuid) { this.uuid = uuid; } diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index fe1b4023c..d11971ddd 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -6,7 +6,10 @@ import com.github.jmchilton.blend4j.galaxy.WorkflowsClient; import com.github.jmchilton.blend4j.galaxy.beans.Workflow; import com.github.jmchilton.blend4j.galaxy.beans.WorkflowDetails; import com.github.jmchilton.blend4j.galaxy.beans.WorkflowInputDefinition; -import com.google.gson.*; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import eu.hbp.mip.controllers.galaxy.retrofit.RetroFitGalaxyClients; import eu.hbp.mip.controllers.galaxy.retrofit.RetrofitClientInstance; import eu.hbp.mip.model.DAOs.ExperimentDAO; @@ -14,11 +17,14 @@ import eu.hbp.mip.model.DAOs.UserDAO; import eu.hbp.mip.model.DTOs.AlgorithmDTO; import eu.hbp.mip.model.DTOs.ExperimentDTO; import eu.hbp.mip.model.UserInfo; -import eu.hbp.mip.model.galaxy.ErrorResponse; import eu.hbp.mip.model.galaxy.GalaxyWorkflowResult; import eu.hbp.mip.model.galaxy.PostWorkflowToGalaxyDtoResponse; import eu.hbp.mip.repositories.ExperimentRepository; -import eu.hbp.mip.utils.*; +import eu.hbp.mip.utils.ClaimUtils; +import eu.hbp.mip.utils.Exceptions.*; +import eu.hbp.mip.utils.HTTPUtil; +import eu.hbp.mip.utils.JsonConverters; +import eu.hbp.mip.utils.Logging; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; @@ -27,8 +33,6 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; import retrofit2.Call; @@ -63,11 +67,24 @@ public class ExperimentService { private static final Gson gson = new Gson(); - public ResponseEntity<String> getExperiments(String name,String algorithm, Boolean shared,Boolean viewed, int page, int size, String endpoint) { + /** + * The getExperiments will retrieve the experiments from database according to the filters. + * + * @param name is optional, in case it is required to filter the experiments by name + * @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 page is the page that is required to be retrieve + * @param size is the size of each page + * @param endpoint is the endpoint that called the function + * @return a list of mapped experiments + */ + public Map getExperiments(String name,String algorithm, Boolean shared,Boolean viewed, int page, int size, String endpoint) { UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Listing my experiments."); if(size > 10 ) - return ResponseEntity.badRequest().body("Invalid size input, max size is 10."); + throw new BadRequestException("Invalid size input, max size is 10."); + Specification<ExperimentDAO> spec = Specification.where(new ExperimentSpecifications.ExperimentWithName(name)) .and(new ExperimentSpecifications.ExperimentWithAlgorithm(algorithm)) @@ -79,7 +96,7 @@ public class ExperimentService { List<ExperimentDAO> experimentDAOs = pageExperiments.getContent(); if (experimentDAOs.isEmpty()) - return new ResponseEntity<>(HttpStatus.NO_CONTENT); + throw new NoContent("No experiment found with the filters provided."); List<ExperimentDTO> experimentDTOs = new ArrayList<>(); experimentDAOs.forEach(experimentDAO -> experimentDTOs.add(experimentDAO.convertToDTO())); @@ -90,55 +107,64 @@ public class ExperimentService { response.put("totalExperiments", pageExperiments.getTotalElements()); response.put("totalPages", pageExperiments.getTotalPages()); - return new ResponseEntity(response, HttpStatus.OK); + return response; } - public ResponseEntity<String> getExperiment(String uuid, String endpoint) { + /** + * The getExperiment will retrieve the experiment from database according to the input uuid + * + * @param uuid is the id of the experiment to be retrieved + * @param endpoint is the endpoint that called the function + * @return the experiment information that was retrieved from the database + */ + public ExperimentDTO getExperiment(String uuid, String endpoint) { ExperimentDAO experimentDAO; UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Loading Experiment with uuid : " + uuid); - experimentDAO = loadExperiment(uuid).orElseThrow(() -> new ResourceNotFoundException("Not found Experimnet with id = " + uuid)); - - if (experimentDAO == null) { - Logging.LogUserAction(user.getUsername(), endpoint, "Experiment Not found."); - return new ResponseEntity<>("Not found", HttpStatus.NOT_FOUND); - } + experimentDAO = loadExperiment(uuid).orElseThrow(() -> new ExperimentNotFoundException("Not found Experimnet with id = " + uuid)); if (!experimentDAO.isShared() && !experimentDAO.getCreatedBy().getUsername().equals(user.getUsername())) { Logging.LogUserAction(user.getUsername(), endpoint, "Accessing Experiment is unauthorized."); - return new ResponseEntity<>("You don't have access to the experiment.", HttpStatus.UNAUTHORIZED); + throw new UnauthorizedException("You don't have access to the experiment."); } ExperimentDTO experimentDTO = experimentDAO.convertToDTO(); Logging.LogUserAction(user.getUsername(), endpoint, "Experiment was Loaded with uuid : " + uuid + "."); - return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); + return experimentDTO; } - public ResponseEntity<String> createExperiment(Authentication authentication, ExperimentDTO experimentDTO, String endpoint) { + /** + * The createExperiment will create and save an experiment in the database. + * + * @param authentication is the role of the user + * @param experimentDTO is the experiment information + * @param endpoint is the endpoint that called the function + * @return the experiment information which was created + */ + public ExperimentDTO createExperiment(Authentication authentication, ExperimentDTO experimentDTO, String endpoint) { UserDAO user = userInfo.getUser(); //Checking if check (POST) /experiments has proper input. if (checkPostExperimentProperInput(experimentDTO)){ Logging.LogUserAction(user.getUsername(), endpoint, "Invalid input."); - return ResponseEntity.badRequest().body("Please provide proper input."); + throw new BadRequestException("Please provide proper input."); } // Get the type and name of algorithm String algorithmType = experimentDTO.getAlgorithmDetails().getType(); String algorithmName = experimentDTO.getAlgorithmDetails().getName(); StringBuilder parametersLogMessage = new StringBuilder(", Parameters:\n"); - for (AlgorithmDTO.AlgorithmParamDTO params : experimentDTO.getAlgorithmDetails().getParameters()) { - parametersLogMessage - .append(" ") - .append(params.getLabel()) - .append(" -> ") - .append(params.getValue()) - .append("\n"); - } + experimentDTO.getAlgorithmDetails().getParameters().forEach( + params -> parametersLogMessage + .append(" ") + .append(params.getLabel()) + .append(" -> ") + .append(params.getValue()) + .append("\n") ); Logging.LogUserAction(user.getUsername(), endpoint, "Executing " + algorithmName + parametersLogMessage); if (authenticationIsEnabled) { @@ -154,12 +180,12 @@ public class ExperimentService { if (experimentDatasets == null || experimentDatasets.equals("")) { Logging.LogUserAction(user.getUsername(), endpoint, "A dataset should be specified to run an algorithm."); - return ResponseEntity.badRequest().body("Please provide at least one dataset to run the algorithm."); + throw new BadRequestException("Please provide at least one dataset to run the algorithm."); } // --- Validating proper access rights on the datasets --- if (!ClaimUtils.userHasDatasetsAuthorization(user.getUsername(), authentication.getAuthorities(), experimentDatasets)) { - return ResponseEntity.badRequest().body("You are not authorized to use these datasets."); + throw new BadRequestException("You are not authorized to use these datasets."); } } @@ -173,7 +199,93 @@ public class ExperimentService { } } - public ResponseEntity<String> updateExperiment(String uuid, ExperimentDTO experimentDTO, String endpoint) + /** + * The createTransientExperiment will run synchronous a transient experiment into exareme and provide results + * + * @param authentication is the role of the user + * @param experimentDTO is the experiment information + * @param endpoint is the endpoint that called the function + * @return the experiment information which was created + */ + public ExperimentDTO createTransientExperiment(Authentication authentication, ExperimentDTO experimentDTO, String endpoint){ + UserDAO user = userInfo.getUser(); + + //Checking if check (POST) /experiments has proper input. + if (checkPostExperimentProperInput(experimentDTO)){ + Logging.LogUserAction(user.getUsername(), endpoint, + "Invalid input."); + throw new BadRequestException("Please provide proper input."); + } + + // Get the parameters + List<AlgorithmDTO.AlgorithmParamDTO> algorithmParameters + = experimentDTO.getAlgorithmDetails().getParameters(); + + // Get the type and name of algorithm + String algorithmName = experimentDTO.getAlgorithmDetails().getName(); + + if (!loadProperAlgorithms().contains(algorithmName)){ + Logging.LogUserAction(user.getUsername(), endpoint, + "Not proper algorithm."); + throw new BadRequestException("Please provide proper algorithm."); + } + + StringBuilder parametersLogMessage = new StringBuilder(", Parameters:\n"); + experimentDTO.getAlgorithmDetails().getParameters().forEach( + params -> parametersLogMessage + .append(" ") + .append(params.getLabel()) + .append(" -> ") + .append(params.getValue()) + .append("\n") ); + Logging.LogUserAction(user.getUsername(), endpoint, "Executing " + algorithmName + parametersLogMessage); + + if (authenticationIsEnabled) { + // Getting the dataset from the experiment parameters + String experimentDatasets = null; + for (AlgorithmDTO.AlgorithmParamDTO parameter : experimentDTO.getAlgorithmDetails().getParameters()) { + if (parameter.getLabel().equals("dataset")) { + experimentDatasets = parameter.getValue(); + break; + } + } + + if (experimentDatasets == null || experimentDatasets.equals("")) { + Logging.LogUserAction(user.getUsername(), endpoint, + "A dataset should be specified to run an algorithm."); + throw new BadRequestException("Please provide at least one dataset to run the algorithm."); + } + + // --- Validating proper access rights on the datasets --- + if (!ClaimUtils.userHasDatasetsAuthorization(user.getUsername(), authentication.getAuthorities(), experimentDatasets)) { + throw new BadRequestException("You are not authorized to use these datasets."); + } + } + + String body = gson.toJson(algorithmParameters); + String url = queryExaremeUrl + "/" + algorithmName; + Logging.LogUserAction(user.getUsername(), endpoint, "url: " + url + ", body: " + body); + + Logging.LogUserAction(user.getUsername(), endpoint, + "Completed, returning: " + experimentDTO.toString()); + + // Results are stored in the experiment object + ExaremeResult exaremeResult = runExaremeExperiment(url, body, experimentDTO); + experimentDTO.setResult(exaremeResult.result); + experimentDTO.setStatus((exaremeResult.code>= 400)? ExperimentDAO.Status.error: ExperimentDAO.Status.success); + + return experimentDTO; + } + + /** + * The updateExperiment will update the experiment's properties + * + * @param uuid is the id of the experiment to be updated + * @param experimentDTO is the experiment information to be updated + * @param endpoint is the endpoint that called the function + * @return the updated experiment information + */ + public ExperimentDTO updateExperiment(String uuid, ExperimentDTO experimentDTO, String endpoint) { ExperimentDAO experimentDAO; UserDAO user = userInfo.getUser(); @@ -182,7 +294,7 @@ public class ExperimentService { if (checkPutExperimentProperInput(experimentDTO)){ Logging.LogUserAction(user.getUsername(), endpoint, "Invalid input."); - return ResponseEntity.badRequest().body("Please provide proper input."); + throw new BadRequestException("Please provide proper input."); } if((experimentDTO.getName() == null || experimentDTO.getName().length() == 0) @@ -190,13 +302,13 @@ public class ExperimentService { && experimentDTO.getViewed() == null && experimentDTO.getAlgorithmDetails() == null) { - return ResponseEntity.badRequest().body("Input is required."); + throw new BadRequestException("Input is required."); } - experimentDAO = loadExperiment(uuid).get(); + experimentDAO = loadExperiment(uuid).orElseThrow(() -> new ExperimentNotFoundException("Not found Experimnet with id = " + uuid)); if (!experimentDAO.getCreatedBy().getUsername().equals(user.getUsername())) - return new ResponseEntity<>("You're not the owner of this experiment", HttpStatus.UNAUTHORIZED); + throw new UnauthorizedException("You don't have access to the experiment."); if(experimentDTO.getName() != null && experimentDTO.getName().length() != 0) { @@ -213,29 +325,39 @@ public class ExperimentService { experimentDAO.setViewed(experimentDTO.getViewed()); } - experimentRepository.save(experimentDAO); - + try { + experimentRepository.save(experimentDAO); + } + catch (Exception e){ + Logging.LogUserAction(user.getUsername(), endpoint, "Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); + throw new InternalServerError(e.getMessage()); + } Logging.LogUserAction(user.getUsername(), endpoint, "Updated experiment with uuid : " + experimentDTO.getUuid() + "."); experimentDTO = experimentDAO.convertToDTO(); - return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); + return experimentDTO; } - public ResponseEntity<String> deleteExperiment(String uuid, String endpoint) + /** + * The deleteExperiment will delete an experiment from the database + * + * @param uuid is the id of the experiment to be deleted + * @param endpoint is the endpoint that called the function + */ + public void deleteExperiment(String uuid, String endpoint) { ExperimentDAO experimentDAO; UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Deleting experiment with uuid : " + uuid + "."); - experimentDAO = loadExperiment(uuid).get(); + experimentDAO = loadExperiment(uuid).orElseThrow(() -> new ExperimentNotFoundException("Not found Experimnet with id = " + uuid)); if (!experimentDAO.getCreatedBy().getUsername().equals(user.getUsername())) - return new ResponseEntity<>(endpoint, HttpStatus.UNAUTHORIZED); + throw new UnauthorizedException("You don't have access to the experiment."); experimentRepository.delete(experimentDAO); Logging.LogUserAction(user.getUsername(), endpoint, "Deleted experiment with uuid : " + uuid + "."); - return new ResponseEntity<>(HttpStatus.NO_CONTENT); } // /* ------------------------------- PRIVATE METHODS ----------------------------------------------------*/ @@ -250,6 +372,14 @@ public class ExperimentService { || experimentDTO.getUuid() != null; } + private List<String> loadProperAlgorithms() + { + List<String> properAlgorithms = new ArrayList<>(); + properAlgorithms.add("histograms"); + properAlgorithms.add("descriptive_stats"); + return properAlgorithms; + } + private boolean checkPutExperimentProperInput(ExperimentDTO experimentDTO) { return experimentDTO.getUuid() != null @@ -258,21 +388,29 @@ public class ExperimentService { || experimentDTO.getStatus() != null; } + /** + * The loadExperiment access the database and load the information of a specific experiment + * + * @param uuid is the id of the experiment to be retrieved + * @return the experiment information that was retrieved from database + */ private Optional<ExperimentDAO> loadExperiment(String uuid){ UUID experimentUuid ; - try { - experimentUuid = UUID.fromString(uuid); - } catch (IllegalArgumentException iae) { - return null; - } + experimentUuid = Optional.of(UUID.fromString(uuid)).orElseThrow(() -> new IllegalArgumentException("Invalid input uuid:"+ uuid)); return experimentRepository.findByUuid(experimentUuid); } - private ExperimentDAO createExperiment(ExperimentDTO experimentDTO, String endpoint) { + /** + * The createExperimentInTheDatabase will insert a new experiment in the database according to the given experiment information + * + * @param experimentDTO is the experiment information to inserted in the database + * @return the experiment information that was inserted into the database + */ + private ExperimentDAO createExperimentInTheDatabase(ExperimentDTO experimentDTO, String endpoint) { UserDAO user = userInfo.getUser(); ExperimentDAO experimentDAO = new ExperimentDAO(); @@ -281,7 +419,14 @@ public class ExperimentService { experimentDAO.setAlgorithmDetails(JsonConverters.convertObjectToJsonString(experimentDTO.getAlgorithmDetails())); experimentDAO.setAlgorithm(experimentDTO.getAlgorithm()); experimentDAO.setName(experimentDTO.getName()); - experimentRepository.save(experimentDAO); + experimentDAO.setStatus(ExperimentDAO.Status.pending); + try { + experimentRepository.save(experimentDAO); + } + catch (Exception e){ + Logging.LogUserAction(user.getUsername(), endpoint, "Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); + throw new InternalServerError(e.getMessage()); + } Logging.LogUserAction(user.getUsername(), endpoint, " id : " + experimentDAO.getUuid()); Logging.LogUserAction(user.getUsername(), endpoint, " algorithms : " + experimentDAO.getAlgorithmDetails()); @@ -298,29 +443,43 @@ public class ExperimentService { Logging.LogUserAction(user.getUsername(), endpoint, " historyId : " + experimentDAO.getWorkflowHistoryId()); Logging.LogUserAction(user.getUsername(), endpoint, " status : " + experimentDAO.getStatus()); - experimentRepository.save(experimentDAO); + try { + experimentRepository.save(experimentDAO); + } + catch (Exception e){ + Logging.LogUserAction(user.getUsername(), endpoint, "Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); + throw new InternalServerError(e.getMessage()); + } Logging.LogUserAction(user.getUsername(), endpoint, "Saved experiment"); } - private void finishExperiment(ExperimentDAO experimentDAO) { + private void finishExperiment(ExperimentDAO experimentDAO, String endpoint) { experimentDAO.setFinished(new Date()); - experimentRepository.save(experimentDAO); + try { + experimentRepository.save(experimentDAO); + } + catch (Exception e){ + Logging.LogUserAction(userInfo.getUser().getUsername(), endpoint, "Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); + throw new InternalServerError(e.getMessage()); + } } /* -------------------------------------- EXAREME CALLS ---------------------------------------------------------*/ /** - * The runExaremeExperiment will POST the algorithm to the exareme client + * The createExaremeExperiment will POST the algorithm to the exareme client * * @param experimentDTO is the request with the experiment information - * @return the response to be returned + * @param endpoint is the endpoint that called the function + * @return the experiment information that was retrieved from exareme */ - public ResponseEntity<String> createExaremeExperiment(ExperimentDTO experimentDTO, String endpoint) { + public ExperimentDTO createExaremeExperiment(ExperimentDTO experimentDTO, String endpoint) { + UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Running the algorithm..."); - ExperimentDAO experimentDAO = createExperiment(experimentDTO, endpoint); + ExperimentDAO experimentDAO = createExperimentInTheDatabase(experimentDTO, endpoint); Logging.LogUserAction(user.getUsername(), endpoint, "Created experiment with uuid :" + experimentDAO.getUuid()); // Run the 1st algorithm from the list @@ -339,41 +498,57 @@ public class ExperimentService { Logging.LogUserAction(user.getUsername(), endpoint, "Starting exareme execution thread"); + ExperimentDTO finalExperimentDTO = experimentDTO; new Thread(() -> { // ATTENTION: Inside the Thread only LogExperimentAction should be used, not LogUserAction! Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Thread named :" + Thread.currentThread().getName() + " with id :" + Thread.currentThread().getId() + " started!"); try { - - // --> New function runExaremeAlgorithm - StringBuilder results = new StringBuilder(); - int code = HTTPUtil.sendPost(url, body, results); - - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Algorithm finished with code: " + code); - // Results are stored in the experiment object - ExperimentDTO.ResultDTO resultDTO = JsonConverters.convertJsonStringToObject(String.valueOf(results), ExperimentDTO.ResultDTO.class); + ExaremeResult exaremeResult = runExaremeExperiment(url, body, finalExperimentDTO); - // <-- New function runExaremeAlgorithm - - experimentDAO.setResult(JsonConverters.convertObjectToJsonString(resultDTO)); -// experimentDAO.setHasError(code >= 400); -// experimentDAO.setHasServerError(code >= 500); + experimentDAO.setResult(JsonConverters.convertObjectToJsonString(exaremeResult.result)); + experimentDAO.setStatus((exaremeResult.code>= 400)? ExperimentDAO.Status.error: ExperimentDAO.Status.success); } catch (Exception e) { Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "There was an exception: " + e.getMessage()); experimentDAO.setStatus(ExperimentDAO.Status.error); - experimentDAO.setResult(e.getMessage()); } - finishExperiment(experimentDAO); + finishExperiment(experimentDAO, endpoint); Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Finished the experiment: " + experimentDAO.toString()); }).start(); experimentDTO = experimentDAO.convertToDTO(); - return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.CREATED); + return experimentDTO; } + /** + * The runExaremeExperiment will run to exareme the experiment + * + * @param url is the url that contain the results of the experiment + * @param body is the parameters of the algorithm + * @param experimentDTO is the experiment information to be executed in the exareme + * @return the result of exareme as well as the http status that was retrieved + */ + public ExaremeResult runExaremeExperiment(String url,String body, ExperimentDTO experimentDTO) { + + StringBuilder results = new StringBuilder(); + int code; + try { + code = HTTPUtil.sendPost(url, body, results); + } + catch (Exception e){ + throw new InternalServerError("Error occured : "+ e.getMessage()); + } + Logging.LogExperimentAction(experimentDTO.getName(), experimentDTO.getUuid(), "Algorithm finished with code: " + code); + + // Results are stored in the experiment object + ExperimentDTO.ResultDTO resultDTO = JsonConverters.convertJsonStringToObject(String.valueOf(results), ExperimentDTO.ResultDTO.class); + return new ExaremeResult(code, resultDTO); + } + + /* --------------------------------------- GALAXY CALLS ---------------------------------------------------------*/ @@ -383,11 +558,11 @@ public class ExperimentService { * @param experimentDTO is the request with the experiment information * @return the response to be returned */ - public ResponseEntity<String> runGalaxyWorkflow(ExperimentDTO experimentDTO, String endpoint) { + public ExperimentDTO runGalaxyWorkflow(ExperimentDTO experimentDTO, String endpoint) { UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Running a workflow..."); - ExperimentDAO experimentDAO = createExperiment(experimentDTO, endpoint); + ExperimentDAO experimentDAO = createExperimentInTheDatabase(experimentDTO, endpoint); Logging.LogUserAction(user.getUsername(), endpoint, "Created experiment with uuid :" + experimentDAO.getUuid()); @@ -419,8 +594,7 @@ public class ExperimentService { if (workflow == null) { Logging.LogUserAction(user.getUsername(), endpoint, "Could not find algorithm code: " + workflowId); - return ResponseEntity.badRequest() - .body(new ErrorResponse("Could not find galaxy algorithm.").toString()); + throw new BadRequestException("Could not find galaxy algorithm."); } final WorkflowDetails workflowDetails = workflowsClient.showWorkflow(workflow.getId()); for (Map.Entry<String, WorkflowInputDefinition> workflowParameter : workflowDetails.getInputs().entrySet()) { @@ -449,8 +623,7 @@ public class ExperimentService { String historyId = (String) new JSONObject(responseBody).get("history_id"); experimentDAO.setWorkflowHistoryId(historyId); - experimentDAO.setStatus(ExperimentDAO.Status.pending); -// experimentDAO.setHasServerError(response.code() >= 500); + experimentDAO.setStatus(ExperimentDAO.Status.success); } else { // Something unexpected happened String msgErr = gson.toJson(response.errorBody()); @@ -460,15 +633,12 @@ public class ExperimentService { JSONObject jObjectError = new JSONObject(msgErr); String errMsg = jObjectError.get("err_msg").toString(); - experimentDAO.setResult(errMsg); -// experimentDAO.setHasError(response.code() >= 400); -// experimentDAO.setHasServerError(response.code() >= 500); + experimentDTO.setStatus((response.code()>= 400)? ExperimentDAO.Status.error: ExperimentDAO.Status.success); } } catch (Exception e) { Logging.LogUserAction(user.getUsername(), endpoint, "An exception occurred: " + e.getMessage()); experimentDAO.setStatus(ExperimentDAO.Status.error); - experimentDAO.setResult(e.getMessage()); } saveExperiment(experimentDAO, endpoint); @@ -478,7 +648,7 @@ public class ExperimentService { Logging.LogUserAction(user.getUsername(), endpoint, "Run workflow completed!"); experimentDTO = experimentDAO.convertToDTO(); - return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.CREATED); + return experimentDTO; } @@ -543,19 +713,20 @@ public class ExperimentService { if (result == null) { experimentDAO.setStatus(ExperimentDAO.Status.error); } - experimentDAO.setResult("[" + result + "]"); - experimentDAO.setStatus(ExperimentDAO.Status.success); - resultFound = true; + else { + experimentDAO.setResult("[" + result + "]"); + experimentDAO.setStatus(ExperimentDAO.Status.success); + resultFound = true; + } } } if (!resultFound) { // If there is no visible result Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "No visible result"); - experimentDAO.setResult("[" + new ErrorResponse("The workflow has no visible result.").toString() + "]"); experimentDAO.setStatus(ExperimentDAO.Status.error); } - finishExperiment(experimentDAO); + finishExperiment(experimentDAO, endpoint); break; case "error": @@ -574,7 +745,6 @@ public class ExperimentService { if (result == null) { experimentDAO.setStatus(ExperimentDAO.Status.error); } - experimentDAO.setResult("[" + result + "]"); experimentDAO.setStatus(ExperimentDAO.Status.error); failedJobFound = true; } @@ -582,16 +752,15 @@ public class ExperimentService { if (!failedJobFound) { // If there is no visible failed job Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "No failed result"); - experimentDAO.setResult("[" + new ErrorResponse("The workflow has no failed result.").toString() + "]"); experimentDAO.setStatus(ExperimentDAO.Status.error); } - finishExperiment(experimentDAO); + finishExperiment(experimentDAO, endpoint); break; default: // InternalError or unexpected result - experimentDAO.setResult("[" + new ErrorResponse("An unexpected error occurred.").toString() + "]"); + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "An unexpected error occurred."); experimentDAO.setStatus(ExperimentDAO.Status.error); - finishExperiment(experimentDAO); + finishExperiment(experimentDAO, endpoint); break; } @@ -624,7 +793,7 @@ public class ExperimentService { RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); Call<Object> call = service.getWorkflowStatusFromGalaxy(historyId, galaxyApiKey); - String result = null; + String result; try { Response<Object> response = call.execute(); if (response.code() >= 400) { @@ -640,7 +809,7 @@ public class ExperimentService { return "internalError"; } - String state = null; + String state; try { JSONObject resultJson = new JSONObject(result); state = resultJson.getString("state"); @@ -679,7 +848,7 @@ public class ExperimentService { RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); Call<List<GalaxyWorkflowResult>> call = service.getWorkflowResultsFromGalaxy(historyId, galaxyApiKey); - List<GalaxyWorkflowResult> getGalaxyWorkflowResultList = null; + List<GalaxyWorkflowResult> getGalaxyWorkflowResultList; try { Response<List<GalaxyWorkflowResult>> response = call.execute(); if (response.code() >= 400) { @@ -717,7 +886,7 @@ public class ExperimentService { Call<Object> call = service.getWorkflowResultsBodyFromGalaxy(historyId, contentId, galaxyApiKey); - String resultJson = null; + String resultJson ; try { Response<Object> response = call.execute(); if (response.code() >= 400) { @@ -751,8 +920,8 @@ public class ExperimentService { RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); Call<Object> callError = service.getErrorMessageOfWorkflowFromGalaxy(jobId, galaxyApiKey); - String fullError = null; - String returnError = null; + String fullError; + String returnError; try { Response<Object> response = callError.execute(); if (response.code() >= 400) { @@ -782,4 +951,22 @@ public class ExperimentService { return returnError; } + + final class ExaremeResult { + private final int code; + private final ExperimentDTO.ResultDTO result; + + public ExaremeResult(int code, ExperimentDTO.ResultDTO result) { + this.code = code; + this.result = result; + } + + public int getCode() { + return code; + } + + public ExperimentDTO.ResultDTO getResult() { + return result; + } + } } diff --git a/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java b/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java index 6596a319a..076249e0b 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java @@ -16,7 +16,7 @@ public class ExperimentSpecifications { public ExperimentWithName(String name){ this.name = name; - this.regExp = "%"+name+"%"; + this.regExp = name; } public Predicate toPredicate(Root<ExperimentDAO> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) @@ -24,8 +24,11 @@ public class ExperimentSpecifications { if (name == null) { return cb.isTrue(cb.literal(true)); } + else { + regExp = (name.contains("%")?name:name+"%"); + } - return cb.like( root.get( "name" ), regExp ); + return cb.like( root.get( "name" ), this.regExp ); } } public static class ExperimentWithAlgorithm implements Specification<ExperimentDAO> { diff --git a/src/main/java/eu/hbp/mip/utils/ControllerExceptionHandler.java b/src/main/java/eu/hbp/mip/utils/ControllerExceptionHandler.java index 4e6f8da32..7f644fb7f 100644 --- a/src/main/java/eu/hbp/mip/utils/ControllerExceptionHandler.java +++ b/src/main/java/eu/hbp/mip/utils/ControllerExceptionHandler.java @@ -1,26 +1,80 @@ package eu.hbp.mip.utils; +import eu.hbp.mip.utils.Exceptions.*; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; import java.util.Date; @ControllerAdvice -public class ControllerExceptionHandler { +public class ControllerExceptionHandler extends ResponseEntityExceptionHandler { + + @ExceptionHandler(ExperimentNotFoundException.class) + public ResponseEntity<Object> handleExperimentNotFoundException(ExperimentNotFoundException ex, WebRequest request) { - @ExceptionHandler(ResourceNotFoundException.class) - public ResponseEntity<ErrorMessage> resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) { ErrorMessage message = new ErrorMessage( - 2, + HttpStatus.NOT_FOUND.value(), new Date(), ex.getMessage(), request.getDescription(false)); return new ResponseEntity<>(message, HttpStatus.NOT_FOUND); } + + @ExceptionHandler(BadRequestException.class) + public ResponseEntity<Object> handleBadRequestException(BadRequestException ex, WebRequest request) { + + ErrorMessage message = new ErrorMessage( + HttpStatus.BAD_REQUEST.value(), + new Date(), + ex.getMessage(), + request.getDescription(false)); + + return new ResponseEntity<>(message, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(UnauthorizedException.class) + public ResponseEntity<Object> handleUnauthorizedException(UnauthorizedException ex, WebRequest request) { + + ErrorMessage message = new ErrorMessage( + HttpStatus.UNAUTHORIZED.value(), + new Date(), + ex.getMessage(), + request.getDescription(false)); + + return new ResponseEntity<>(message, HttpStatus.UNAUTHORIZED); + } + + @ExceptionHandler(IllegalArgumentException.class) + public Object handleIllegalArgumentException(IllegalArgumentException ex, WebRequest request) { + return null; + } + + @ExceptionHandler(InternalServerError.class) + public ResponseEntity<ErrorMessage> handleInternalServerError(InternalServerError er, WebRequest request) { + ErrorMessage message = new ErrorMessage( + HttpStatus.INTERNAL_SERVER_ERROR.value(), + new Date(), + er.getMessage(), + request.getDescription(false)); + + return new ResponseEntity<>(message, HttpStatus.INTERNAL_SERVER_ERROR); + } + + @ExceptionHandler(NoContent.class) + public ResponseEntity<ErrorMessage> handleNoContent(NoContent nc, WebRequest request) { + ErrorMessage message = new ErrorMessage( + HttpStatus.NO_CONTENT.value(), + new Date(), + nc.getMessage(), + request.getDescription(false)); + + return new ResponseEntity<>(message, HttpStatus.NO_CONTENT); + } @ExceptionHandler(Exception.class) public ResponseEntity<ErrorMessage> globalExceptionHandler(Exception ex, WebRequest request) { diff --git a/src/main/java/eu/hbp/mip/utils/Exceptions/BadRequestException.java b/src/main/java/eu/hbp/mip/utils/Exceptions/BadRequestException.java new file mode 100644 index 000000000..6684cae28 --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/Exceptions/BadRequestException.java @@ -0,0 +1,8 @@ +package eu.hbp.mip.utils.Exceptions; + +public class BadRequestException extends RuntimeException { + + public BadRequestException(String msg) { + super(msg); + } +} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/utils/Exceptions/ExperimentNotFoundException.java b/src/main/java/eu/hbp/mip/utils/Exceptions/ExperimentNotFoundException.java new file mode 100644 index 000000000..b8e05d7e6 --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/Exceptions/ExperimentNotFoundException.java @@ -0,0 +1,8 @@ +package eu.hbp.mip.utils.Exceptions; + +public class ExperimentNotFoundException extends RuntimeException { + + public ExperimentNotFoundException(String msg) { + super(msg); + } +} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/utils/Exceptions/InternalServerError.java b/src/main/java/eu/hbp/mip/utils/Exceptions/InternalServerError.java new file mode 100644 index 000000000..19bc88eed --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/Exceptions/InternalServerError.java @@ -0,0 +1,8 @@ +package eu.hbp.mip.utils.Exceptions; + +public class InternalServerError extends RuntimeException { + + public InternalServerError(String msg) { + super(msg); + } +} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/utils/Exceptions/NoContent.java b/src/main/java/eu/hbp/mip/utils/Exceptions/NoContent.java new file mode 100644 index 000000000..9bb67d811 --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/Exceptions/NoContent.java @@ -0,0 +1,8 @@ +package eu.hbp.mip.utils.Exceptions; + +public class NoContent extends RuntimeException { + + public NoContent(String msg) { + super(msg); + } +} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/utils/Exceptions/UnauthorizedException.java b/src/main/java/eu/hbp/mip/utils/Exceptions/UnauthorizedException.java new file mode 100644 index 000000000..379911dae --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/Exceptions/UnauthorizedException.java @@ -0,0 +1,8 @@ +package eu.hbp.mip.utils.Exceptions; + +public class UnauthorizedException extends RuntimeException { + + public UnauthorizedException(String msg) { + super(msg); + } +} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/utils/ResourceNotFoundException.java b/src/main/java/eu/hbp/mip/utils/ResourceNotFoundException.java deleted file mode 100644 index 38f067c42..000000000 --- a/src/main/java/eu/hbp/mip/utils/ResourceNotFoundException.java +++ /dev/null @@ -1,10 +0,0 @@ -package eu.hbp.mip.utils; - -public class ResourceNotFoundException extends RuntimeException { - - private static final long serialVersionUID = 1L; - - public ResourceNotFoundException(String msg) { - super(msg); - } -} \ No newline at end of file diff --git a/src/main/resources/db/migration/V7_0__NewExperimentStructure.sql b/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql similarity index 100% rename from src/main/resources/db/migration/V7_0__NewExperimentStructure.sql rename to src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql -- GitLab From 0d48ca86b1c3ef494dc8a1ca14f7e216252bdc43 Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Wed, 11 Nov 2020 05:19:52 -0800 Subject: [PATCH 19/52] Fixing 404 message on login as well as on swagger. --- pom.xml | 4 ++++ src/main/java/eu/hbp/mip/configuration/WebConfiguration.java | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e5d87b28f..e50a9bf47 100644 --- a/pom.xml +++ b/pom.xml @@ -66,6 +66,10 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-data-rest</artifactId> + </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> diff --git a/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java b/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java index 17e38a5ee..72bcad789 100644 --- a/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java @@ -2,7 +2,6 @@ package eu.hbp.mip.configuration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.data.web.config.EnableSpringDataWebSupport; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; @@ -17,7 +16,6 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration @EnableSwagger2 -@EnableSpringDataWebSupport public class WebConfiguration { // @Bean -- GitLab From 4a162ad3d37bf94c69993dac520ec671b135930f Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Wed, 11 Nov 2020 11:51:08 -0800 Subject: [PATCH 20/52] Security API refactoring. 1) User API moved from Security API> 2) UserInfo is not activeUserService. 3) User Details read from Keycloak Authentication Token. 4) Added application.yml to develop the portal-backend without dockerizing. --- docker/README.md | 2 +- pom.xml | 1 + .../configuration/SecurityConfiguration.java | 8 +- .../eu/hbp/mip/controllers/AlgorithmsApi.java | 36 ++++---- .../eu/hbp/mip/controllers/ExperimentApi.java | 4 +- .../hbp/mip/controllers/PathologiesApi.java | 14 +-- .../eu/hbp/mip/controllers/SecurityApi.java | 75 +++------------- .../java/eu/hbp/mip/controllers/UsersApi.java | 63 +++++++++++--- .../java/eu/hbp/mip/model/DAOs/UserDAO.java | 57 +++---------- .../java/eu/hbp/mip/model/GeneralStats.java | 41 --------- src/main/java/eu/hbp/mip/model/UserInfo.java | 85 ------------------- .../hbp/mip/services/ActiveUserService.java | 73 ++++++++++++++++ .../hbp/mip/services/ExperimentService.java | 27 +++--- src/main/resources/application.yml | 84 ++++++++++++++++++ src/main/resources/test.test | 64 ++++++++++++++ 15 files changed, 341 insertions(+), 293 deletions(-) delete mode 100644 src/main/java/eu/hbp/mip/model/GeneralStats.java delete mode 100644 src/main/java/eu/hbp/mip/model/UserInfo.java create mode 100644 src/main/java/eu/hbp/mip/services/ActiveUserService.java create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/test.test diff --git a/docker/README.md b/docker/README.md index cc8c59fe1..da8299413 100644 --- a/docker/README.md +++ b/docker/README.md @@ -33,7 +33,7 @@ To use this image, you need a running instance of PostgreSQL and to configure th * CLIENT_SECRET: required when authentication is turned on, client secret for the [OpenID server of HBP](https://services.humanbrainproject.eu/oidc/). * TOKEN_URI: default to "https://services.humanbrainproject.eu/oidc/token". * AUTH_URI: default to "https://services.humanbrainproject.eu/oidc/authorize". -* USER_INFO_URI: default to "https://services.humanbrainproject.eu/oidc/userinfo". +* USER_INFO_URI: default to "https://services.humanbrainproject.eu/oidc/userInfo". * REVOKE_TOKEN_URI "https://services.humanbrainproject.eu/oidc/slo". diff --git a/pom.xml b/pom.xml index e50a9bf47..5292dffd0 100644 --- a/pom.xml +++ b/pom.xml @@ -274,6 +274,7 @@ <include>**/*.csv</include> <include>**/*.sql</include> <include>**/*.conf</include> + <include>**/*.yml</include> <!-- Only for development --> </includes> <filtering>true</filtering> </resource> diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index d2a0dd739..22f904af5 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -99,7 +99,6 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) { SimpleAuthorityMapper grantedAuthorityMapper = new SimpleAuthorityMapper(); - //grantedAuthorityMapper.setPrefix("ROLE_"); grantedAuthorityMapper.setConvertToUpperCase(true); KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider(); @@ -125,6 +124,7 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter http.antMatcher("/**") .authorizeRequests() .antMatchers("/**").permitAll() + .and().csrf().disable(); // .antMatchers( // "/login**", "/health/**", "/info/**", "/metrics/**", // "/trace/**", "/frontend/**", "/webjars/**", "/v2/api-docs", @@ -134,8 +134,8 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter // .anyRequest().hasRole("RESEARCHER") // .and().exceptionHandling().authenticationEntryPoint(new CustomLoginUrlAuthenticationEntryPoint(loginUrl)) // .accessDeniedHandler(new CustomAccessDeniedHandler()) - .and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository()) - .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class).csrf(); +// .and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository()) +// .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class).csrf(); // .and().logout().logoutSuccessUrl("/logout"); // // @@ -171,7 +171,7 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter // OAuth2RestTemplate hbpTemplate = new OAuth2RestTemplate(hbp(), oauth2ClientContext); // hbpFilter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(frontendRedirectAfterLogin)); // hbpFilter.setRestTemplate(hbpTemplate); -// hbpFilter.setTokenServices(new UserInfoTokenServices(hbpResource().getUserInfoUri(), hbp().getClientId())); +// hbpFilter.setTokenServices(new activeUserServiceTokenServices(hbpResource().getactiveUserServiceUri(), hbp().getClientId())); // return hbpFilter; // } diff --git a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java index d5dca19ae..51fc1b485 100644 --- a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java +++ b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java @@ -9,7 +9,7 @@ import com.google.gson.reflect.TypeToken; import eu.hbp.mip.controllers.galaxy.retrofit.RetroFitGalaxyClients; import eu.hbp.mip.controllers.galaxy.retrofit.RetrofitClientInstance; import eu.hbp.mip.model.DTOs.AlgorithmDTO; -import eu.hbp.mip.model.UserInfo; +import eu.hbp.mip.services.ActiveUserService; import eu.hbp.mip.model.galaxy.WorkflowDTO; import eu.hbp.mip.utils.CustomResourceLoader; import eu.hbp.mip.utils.HTTPUtil; @@ -42,7 +42,7 @@ public class AlgorithmsApi { private static final Gson gson = new Gson(); @Autowired - private UserInfo userInfo; + private ActiveUserService activeUserService; @Value("#{'${services.exareme.algorithmsUrl}'}") private String exaremeAlgorithmsUrl; @@ -56,24 +56,24 @@ public class AlgorithmsApi { @ApiOperation(value = "List all algorithms", response = String.class) @RequestMapping(method = RequestMethod.GET) public ResponseEntity<List<AlgorithmDTO>> getAlgorithms() { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "Executing..."); + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /algorithms", "Executing..."); LinkedList<AlgorithmDTO> exaremeAlgorithms = getExaremeAlgorithms(); - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "Loaded " + exaremeAlgorithms.size() + " exareme algorithms"); + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /algorithms", "Loaded " + exaremeAlgorithms.size() + " exareme algorithms"); LinkedList<AlgorithmDTO> galaxyAlgorithms = getGalaxyWorkflows(); - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "Loaded " + galaxyAlgorithms.size() + " galaxy algorithms"); + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /algorithms", "Loaded " + galaxyAlgorithms.size() + " galaxy algorithms"); LinkedList<AlgorithmDTO> algorithms = new LinkedList<>(); if (exaremeAlgorithms != null) { algorithms.addAll(exaremeAlgorithms); } else { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /algorithms", "Getting exareme algorithms failed and returned null"); } if (galaxyAlgorithms != null) { algorithms.addAll(galaxyAlgorithms); } else { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /algorithms", "Getting galaxy workflows failed and returned null"); } @@ -81,7 +81,7 @@ public class AlgorithmsApi { try { disabledAlgorithms = getDisabledAlgorithms(); } catch (IOException e) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /algorithms", "The disabled algorithms could not be loaded."); } @@ -92,7 +92,7 @@ public class AlgorithmsApi { allowedAlgorithms.add(algorithm); } } - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /algorithms", "Successfully listed " + allowedAlgorithms.size() + " algorithms"); return ResponseEntity.ok(allowedAlgorithms); } @@ -115,11 +115,11 @@ public class AlgorithmsApi { }.getType() ); } catch (IOException e) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "An exception occurred: " + e.getMessage()); + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /algorithms", "An exception occurred: " + e.getMessage()); return null; } - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /algorithms", "Completed, returned " + algorithms.size() + " algorithms."); return algorithms; } @@ -139,7 +139,7 @@ public class AlgorithmsApi { workflowList = new ArrayList<>(workflowsClient.getWorkflows()); } catch (Exception e) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "Error when calling list galaxy workflows: " + e.getMessage()); + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /algorithms", "Error when calling list galaxy workflows: " + e.getMessage()); return null; } @@ -159,28 +159,28 @@ public class AlgorithmsApi { } else { // Something unexpected happened String msgErr = gson.toJson(response.errorBody()); - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "Error Response: " + msgErr); + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /algorithms", "Error Response: " + msgErr); return null; } } catch (Exception e) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "An exception occurred: " + e.getMessage()); + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /algorithms", "An exception occurred: " + e.getMessage()); return null; } } - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "Workflows fetched: " + workflows.size()); + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /algorithms", "Workflows fetched: " + workflows.size()); // Convert the workflows to algorithms LinkedList<AlgorithmDTO> algorithms = new LinkedList<>(); for (WorkflowDTO workflow : workflows) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "Converting workflow: " + workflow); + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /algorithms", "Converting workflow: " + workflow); algorithms.add(workflow.convertToAlgorithmDTO()); - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /algorithms", "Converted algorithm: " + algorithms.get(algorithms.size() - 1)); } - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "Completed!"); + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /algorithms", "Completed!"); return algorithms; } diff --git a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java index 322f33f4f..d5fc161b1 100644 --- a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java +++ b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java @@ -1,7 +1,7 @@ package eu.hbp.mip.controllers; import eu.hbp.mip.model.DTOs.ExperimentDTO; -import eu.hbp.mip.model.UserInfo; +import eu.hbp.mip.services.ActiveUserService; import eu.hbp.mip.services.ExperimentService; import eu.hbp.mip.utils.JsonConverters; import io.swagger.annotations.Api; @@ -28,7 +28,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; public class ExperimentApi { @Autowired - private UserInfo userInfo; + private ActiveUserService activeUserService; @Autowired private ExperimentService experimentService; diff --git a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java index 6c89e1b94..055b04b23 100644 --- a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java +++ b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java @@ -7,7 +7,7 @@ package eu.hbp.mip.controllers; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import eu.hbp.mip.model.DTOs.PathologyDTO; -import eu.hbp.mip.model.UserInfo; +import eu.hbp.mip.services.ActiveUserService; import eu.hbp.mip.utils.ClaimUtils; import eu.hbp.mip.utils.CustomResourceLoader; import eu.hbp.mip.utils.Exceptions.BadRequestException; @@ -36,7 +36,7 @@ public class PathologiesApi { private static final Gson gson = new Gson(); @Autowired - private UserInfo userInfo; + private ActiveUserService activeUserService; // Enable HBP collab authentication (1) or disable it (0). Default is 1 @Value("#{'${authentication.enabled}'}") @@ -47,7 +47,7 @@ public class PathologiesApi { @RequestMapping(name = "/pathologies", method = RequestMethod.GET) public ResponseEntity<String> getPathologies(Authentication authentication) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /pathologies", "Loading pathologies ..."); + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /pathologies", "Loading pathologies ..."); // Load pathologies from file Resource resource = resourceLoader.getResource("file:/opt/portal/api/pathologies.json"); @@ -56,18 +56,18 @@ public class PathologiesApi { allPathologies = gson.fromJson(InputStreamConverter.convertInputStreamToString(resource.getInputStream()), new TypeToken<List<PathologyDTO>>() { }.getType()); } catch (IOException e) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /pathologies", "Unable to load pathologies"); + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /pathologies", "Unable to load pathologies"); throw new BadRequestException("The pathologies could not be loaded."); } // If authentication is disabled return everything if (!authenticationIsEnabled) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /pathologies", "Successfully loaded " + allPathologies.size() + " pathologies"); + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /pathologies", "Successfully loaded " + allPathologies.size() + " pathologies"); return ResponseEntity.ok().body(gson.toJson(allPathologies)); } - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /pathologies", "Successfully loaded all authorized pathologies"); + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /pathologies", "Successfully loaded all authorized pathologies"); return ResponseEntity.ok().body(ClaimUtils.getAuthorizedPathologies( - userInfo.getUser().getUsername(), authentication.getAuthorities(), allPathologies)); + activeUserService.getActiveUser().getUsername(), authentication.getAuthorities(), allPathologies)); } } diff --git a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java index 9d2b66492..696fac039 100644 --- a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java +++ b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java @@ -1,29 +1,23 @@ package eu.hbp.mip.controllers; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.google.gson.JsonObject; import eu.hbp.mip.configuration.SecurityConfiguration; -import eu.hbp.mip.model.DAOs.UserDAO; -import eu.hbp.mip.model.UserInfo; -import eu.hbp.mip.repositories.UserRepository; +import eu.hbp.mip.services.ActiveUserService; import eu.hbp.mip.utils.Logging; -import io.swagger.annotations.ApiParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; -import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.security.Principal; import java.util.Base64; @RestController @@ -32,64 +26,21 @@ public class SecurityApi { private static final Gson gson = new Gson(); @Autowired - private UserInfo userInfo; - - @Autowired - private UserRepository userRepository; + private ActiveUserService activeUserService; @Autowired private SecurityConfiguration securityConfiguration; - @RequestMapping(path = "/user", method = RequestMethod.GET) - public Object user(Principal principal, HttpServletResponse response) { - ObjectMapper mapper = new ObjectMapper(); - - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /user", "Loading user : " + userInfo.getUser().getUsername()); - try { - String userJSON = mapper.writeValueAsString(userInfo.getUser()); - Cookie cookie = new Cookie("user", URLEncoder.encode(userJSON, "UTF-8")); - cookie.setSecure(true); - cookie.setPath("/"); - response.addCookie(cookie); - } catch (JsonProcessingException | UnsupportedEncodingException e) { - //LOGGER.trace("Cannot read user json", e); - } - - if (!securityConfiguration.getAuthenticationEnabled()) { - if (userInfo.getFakeAuth()) { - response.setStatus(401); - } - String principalJson = "{\"principal\": \"anonymous\", \"name\": \"anonymous\", \"userAuthentication\": {" - + "\"details\": {\"preferred_username\": \"anonymous\"}}}"; - return new Gson().fromJson(principalJson, Object.class); - } - return principal; - } - - @RequestMapping(path = "/user", method = RequestMethod.POST) - public ResponseEntity<Void> postUser( - @ApiParam(value = "Has the user agreed on the NDA") @RequestParam(value = "agreeNDA") Boolean agreeNDA) { - UserDAO user = userInfo.getUser(); - if (user != null) { - user.setAgreeNDA(agreeNDA); - userRepository.save(user); - } + // TODO How to redirect? keycloak off? + @RequestMapping(path = "/login/hbp", method = RequestMethod.GET) + @ConditionalOnExpression("${authentication.enabled:0}") + public void noLogin(HttpServletResponse httpServletResponse) throws IOException { + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /user/login/hbp", "Unauthorized login."); - Logging.LogUserAction(userInfo.getUser().getUsername(), "(POST) /user", "User has agreed on the NDA"); - - return new ResponseEntity<>(HttpStatus.NO_CONTENT); + httpServletResponse.sendRedirect(securityConfiguration.getFrontendRedirectAfterLogin()); } -// @RequestMapping(path = "/login/hbp", method = RequestMethod.GET) -// @ConditionalOnExpression("${authentication.enabled:0}") -// public void noLogin(HttpServletResponse httpServletResponse) throws IOException { -// userInfo.setFakeAuth(true); -// Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /user/login/hbp", "Unauthorized login."); -// -// httpServletResponse.sendRedirect(securityConfiguration.getFrontendRedirectAfterLogin()); -// } - @Value("#{'${services.galaxy.galaxyUsername:admin}'}") private String galaxyUsername; @@ -112,7 +63,7 @@ public class SecurityApi { JsonObject object = new JsonObject(); object.addProperty("authorization", stringEncoded); object.addProperty("context", galaxyContext); - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /user/galaxy", "Successfully Loaded galaxy information."); + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /user/galaxy", "Successfully Loaded galaxy information."); return ResponseEntity.ok(gson.toJson(object)); } diff --git a/src/main/java/eu/hbp/mip/controllers/UsersApi.java b/src/main/java/eu/hbp/mip/controllers/UsersApi.java index 886ad1ac9..66f34b670 100644 --- a/src/main/java/eu/hbp/mip/controllers/UsersApi.java +++ b/src/main/java/eu/hbp/mip/controllers/UsersApi.java @@ -1,43 +1,80 @@ -/* - * Created by mirco on 14.01.16. - */ - package eu.hbp.mip.controllers; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import eu.hbp.mip.model.DAOs.UserDAO; -import eu.hbp.mip.model.UserInfo; import eu.hbp.mip.repositories.UserRepository; +import eu.hbp.mip.services.ActiveUserService; import eu.hbp.mip.utils.Logging; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @RestController @RequestMapping(value = "/users", produces = {APPLICATION_JSON_VALUE}) -@Api(value = "/users", description = "the users API") +@Api(value = "/users") public class UsersApi { @Autowired - private UserRepository userRepository; + private ActiveUserService activeUserService; @Autowired - private UserInfo userInfo; + private UserRepository userRepository; @ApiOperation(value = "Get a user", response = UserDAO.class) @RequestMapping(value = "/{username}", method = RequestMethod.GET) public ResponseEntity<UserDAO> getAUser( @ApiParam(value = "username", required = true) @PathVariable("username") String username ) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /users/{username}", "Loaded a user with username : " + userInfo.getUser().getUsername()); + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /users/{username}", + "Loaded a user with username : " + username); + // TODO Error handling? return ResponseEntity.ok(userRepository.findByUsername(username)); } + + @ApiOperation(value = "Get the active user", response = UserDAO.class) + @RequestMapping(value = "/activeUser", method = RequestMethod.GET) + public ResponseEntity<UserDAO> getTheActiveUser(HttpServletResponse response) { + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /users/activeUser", + "Loading the details of the activeUser"); + + UserDAO activeUser = activeUserService.getActiveUser(); + + // Add the active user to a cookie + try { + // TODO needed? Ask Manuel + ObjectMapper mapper = new ObjectMapper(); + String userJSON = mapper.writeValueAsString(activeUser); + Cookie cookie = new Cookie("user", URLEncoder.encode(userJSON, "UTF-8")); + cookie.setSecure(true); + cookie.setPath("/"); + response.addCookie(cookie); + } catch (JsonProcessingException | UnsupportedEncodingException e) { + Logging.LogUserAction(activeUser.getUsername(), + "(GET) /users/activeUser", "Failed to add Cookie. Exception: " + e.getMessage()); + } + + return ResponseEntity.ok(activeUserService.getActiveUser()); + } + + // TODO Kostas, why not working? + @ApiOperation(value = "The active user agrees to the NDA", response = UserDAO.class) + @RequestMapping(value = "/activeUser/agreeNDA", method = RequestMethod.POST) + public ResponseEntity<UserDAO> activeUserServiceAgreesToNDA(@RequestBody(required = false) UserDAO userDAO) { + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /users/activeUser/agreeNDA", + "The user agreed to the NDA"); + + return ResponseEntity.ok(activeUserService.agreeToNDA()); + } } diff --git a/src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java b/src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java index 0d0ba246d..0e1e9a9ff 100644 --- a/src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java +++ b/src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java @@ -20,63 +20,28 @@ public class UserDAO { @Id @Expose - private String username = null; + private String username; @Expose - private String fullname = null; + private String fullname; @Expose - private String email = null; - - private Boolean agreeNDA = null; + private String email; + @Expose + private Boolean agreeNDA; public UserDAO() { - /* - * Empty constructor is needed by Hibernate - */ + // Empty constructor is needed by Hibernate } - - /** - * Create a user using OpenID user profile - * - * @param userInfo info from OpenID UserInfo endpoint - */ - public UserDAO(String userInfo) { - - - // TODO fix - this.username = "test"; - this.fullname = "test"; - this.email = "test"; - - // -// Pattern p; -// Matcher m; -// -// p = Pattern.compile("preferred_username=([\\w- ]+)"); -// m = p.matcher(userInfo); -// if (m.find()) { -// this.username = m.group(1); -// } -// -// p = Pattern.compile("name=([\\w ]+)"); -// m = p.matcher(userInfo); -// if (m.find()) { -// this.fullname = m.group(1); -// } -// -// p = Pattern.compile("email=([\\w.]+@[\\w.]+)"); -// m = p.matcher(userInfo); -// if (m.find()) { -// this.email = m.group(1); -// } - - + public UserDAO(String username, String fullname, String email) { + this.username = username; + this.fullname = fullname; + this.email = email; + this.agreeNDA = false; } - public String getFullname() { return fullname; } diff --git a/src/main/java/eu/hbp/mip/model/GeneralStats.java b/src/main/java/eu/hbp/mip/model/GeneralStats.java deleted file mode 100644 index 4253ae7dd..000000000 --- a/src/main/java/eu/hbp/mip/model/GeneralStats.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Created by mirco on 03.02.16. - */ - -package eu.hbp.mip.model; - -import com.fasterxml.jackson.annotation.JsonInclude; -import io.swagger.annotations.ApiModel; - -@ApiModel -@JsonInclude(JsonInclude.Include.NON_NULL) -public class GeneralStats { - - private Long users = null; - private Long articles = null; - - - public GeneralStats() { - /* - * Empty constructor is needed by Hibernate - */ - } - - - public Long getUsers() { - return users; - } - - public void setUsers(Long users) { - this.users = users; - } - - - public Long getArticles() { - return articles; - } - - public void setArticles(Long articles) { - this.articles = articles; - } -} diff --git a/src/main/java/eu/hbp/mip/model/UserInfo.java b/src/main/java/eu/hbp/mip/model/UserInfo.java deleted file mode 100644 index 617b25a5f..000000000 --- a/src/main/java/eu/hbp/mip/model/UserInfo.java +++ /dev/null @@ -1,85 +0,0 @@ -package eu.hbp.mip.model; - -import eu.hbp.mip.model.DAOs.UserDAO; -import eu.hbp.mip.repositories.UserRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Scope; -import org.springframework.context.annotation.ScopedProxyMode; -import org.springframework.stereotype.Component; - -import javax.inject.Named; - -@Component -@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) -@Named("userInfo") -public class UserInfo { - - @Autowired - private UserRepository userRepository; - - /** - * Enable HBP collab authentication (1) or disable it (0). Default is 1 - */ - @Value("#{'${authentication.enabled}'}") - private boolean authentication; - - private UserDAO user; - - /** - * Set to true if using no-auth mode and user has clicked on the login button - */ - private boolean fakeAuth = false; - - /** - * returns the user for the current session. - * <p> - * the "synchronized" keyword is there to avoid a bug that the transaction is supposed to protect me from. - * To test if your solution to removing it works, do the following: - * - clean DB from scratch - * - restart DB and backend (no session or anything like that) - * - log in using the front end - * - check you have no 500 error in the network logs. - * - * @return the user for the current session - */ - public UserDAO getUser() { - if (user == null) { - - if (!authentication) { - user = new UserDAO(); - user.setUsername("anonymous"); - user.setFullname("anonymous"); - user.setEmail("anonymous@anonymous.com"); - } else { - user = new UserDAO(getUserInformation()); - } - - UserDAO foundUser = userRepository.findByUsername(user.getUsername()); - if (foundUser != null) { - user.setAgreeNDA(foundUser.getAgreeNDA()); - } - userRepository.save(user); - } - - return user; - } - - public boolean getFakeAuth() { - return fakeAuth; - } - - public void setFakeAuth(boolean fakeAuth) { - this.fakeAuth = fakeAuth; - } - - private String getUserInformation() { - // TODO - return ""; - // get details from keycloak configuration -// OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication(); -// Authentication userAuthentication = oAuth2Authentication.getUserAuthentication(); -// return userAuthentication.getDetails().toString(); - } - -} diff --git a/src/main/java/eu/hbp/mip/services/ActiveUserService.java b/src/main/java/eu/hbp/mip/services/ActiveUserService.java new file mode 100644 index 000000000..d925ce3f5 --- /dev/null +++ b/src/main/java/eu/hbp/mip/services/ActiveUserService.java @@ -0,0 +1,73 @@ +package eu.hbp.mip.services; + +import eu.hbp.mip.model.DAOs.UserDAO; +import eu.hbp.mip.repositories.UserRepository; +import org.keycloak.KeycloakPrincipal; +import org.keycloak.representations.IDToken; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; + +import javax.inject.Named; + +@Component +@Named("ActiveUserService") +@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) +public class ActiveUserService { + + @Autowired + private UserRepository userRepository; + + @Value("#{'${authentication.enabled}'}") + private boolean authentication; + + private UserDAO user; + + /** + * Fetches the details of the active user. + * If the user doesn't exist, it's created on the fly from the auth token. + * + * @return the userDAO + */ + public UserDAO getActiveUser() { + + // User already loaded + if (user != null) + return user; + + // If Authentication is OFF, create anonymous user with accepted NDA + if (!authentication) { + user = new UserDAO("anonymous", "anonymous", "anonymous@anonymous.com"); + user.setAgreeNDA(true); + userRepository.save(user); + return user; + } + + // If authentication is ON + KeycloakPrincipal keycloakPrincipal = + (KeycloakPrincipal) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + IDToken idToken = keycloakPrincipal.getKeycloakSecurityContext().getIdToken(); + UserDAO userInDatabase = userRepository.findByUsername(idToken.getPreferredUsername()); + if (userInDatabase != null) { + user = userInDatabase; + } else { + UserDAO newUser = new UserDAO(idToken.getPreferredUsername(), idToken.getName(), idToken.getEmail()); + userRepository.save(newUser); + user = newUser; + } + return user; + } + + public UserDAO agreeToNDA() { + // Fetch the active user + getActiveUser(); + + user.setAgreeNDA(true); + userRepository.save(user); + + return user; + } +} diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index d11971ddd..7ffffc7eb 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -16,7 +16,6 @@ import eu.hbp.mip.model.DAOs.ExperimentDAO; import eu.hbp.mip.model.DAOs.UserDAO; import eu.hbp.mip.model.DTOs.AlgorithmDTO; import eu.hbp.mip.model.DTOs.ExperimentDTO; -import eu.hbp.mip.model.UserInfo; import eu.hbp.mip.model.galaxy.GalaxyWorkflowResult; import eu.hbp.mip.model.galaxy.PostWorkflowToGalaxyDtoResponse; import eu.hbp.mip.repositories.ExperimentRepository; @@ -47,7 +46,7 @@ import static java.lang.Thread.sleep; public class ExperimentService { @Autowired - private UserInfo userInfo; + private ActiveUserService activeUserService; @Value("#{'${services.exareme.queryExaremeUrl}'}") private String queryExaremeUrl; @@ -80,7 +79,7 @@ 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 endpoint) { - UserDAO user = userInfo.getUser(); + 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."); @@ -120,7 +119,7 @@ public class ExperimentService { public ExperimentDTO getExperiment(String uuid, String endpoint) { ExperimentDAO experimentDAO; - UserDAO user = userInfo.getUser(); + UserDAO user = activeUserService.getActiveUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Loading Experiment with uuid : " + uuid); @@ -145,7 +144,7 @@ public class ExperimentService { * @return the experiment information which was created */ public ExperimentDTO createExperiment(Authentication authentication, ExperimentDTO experimentDTO, String endpoint) { - UserDAO user = userInfo.getUser(); + UserDAO user = activeUserService.getActiveUser(); //Checking if check (POST) /experiments has proper input. if (checkPostExperimentProperInput(experimentDTO)){ @@ -208,7 +207,7 @@ public class ExperimentService { * @return the experiment information which was created */ public ExperimentDTO createTransientExperiment(Authentication authentication, ExperimentDTO experimentDTO, String endpoint){ - UserDAO user = userInfo.getUser(); + UserDAO user = activeUserService.getActiveUser(); //Checking if check (POST) /experiments has proper input. if (checkPostExperimentProperInput(experimentDTO)){ @@ -288,7 +287,7 @@ public class ExperimentService { public ExperimentDTO updateExperiment(String uuid, ExperimentDTO experimentDTO, String endpoint) { ExperimentDAO experimentDAO; - UserDAO user = userInfo.getUser(); + UserDAO user = activeUserService.getActiveUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Updating experiment with uuid : " + experimentDTO.getUuid() + "."); //Checking if check (PUT) /experiments has proper input. if (checkPutExperimentProperInput(experimentDTO)){ @@ -347,7 +346,7 @@ public class ExperimentService { public void deleteExperiment(String uuid, String endpoint) { ExperimentDAO experimentDAO; - UserDAO user = userInfo.getUser(); + UserDAO user = activeUserService.getActiveUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Deleting experiment with uuid : " + uuid + "."); experimentDAO = loadExperiment(uuid).orElseThrow(() -> new ExperimentNotFoundException("Not found Experimnet with id = " + uuid)); @@ -411,7 +410,7 @@ public class ExperimentService { * @return the experiment information that was inserted into the database */ private ExperimentDAO createExperimentInTheDatabase(ExperimentDTO experimentDTO, String endpoint) { - UserDAO user = userInfo.getUser(); + UserDAO user = activeUserService.getActiveUser(); ExperimentDAO experimentDAO = new ExperimentDAO(); experimentDAO.setUuid(UUID.randomUUID()); @@ -435,7 +434,7 @@ public class ExperimentService { } private void saveExperiment(ExperimentDAO experimentDAO, String endpoint) { - UserDAO user = userInfo.getUser(); + UserDAO user = activeUserService.getActiveUser(); Logging.LogUserAction(user.getUsername(), endpoint, " id : " + experimentDAO.getUuid()); Logging.LogUserAction(user.getUsername(), endpoint, " algorithms : " + experimentDAO.getAlgorithmDetails()); @@ -460,7 +459,7 @@ public class ExperimentService { experimentRepository.save(experimentDAO); } catch (Exception e){ - Logging.LogUserAction(userInfo.getUser().getUsername(), endpoint, "Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); throw new InternalServerError(e.getMessage()); } } @@ -476,7 +475,7 @@ public class ExperimentService { */ public ExperimentDTO createExaremeExperiment(ExperimentDTO experimentDTO, String endpoint) { - UserDAO user = userInfo.getUser(); + UserDAO user = activeUserService.getActiveUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Running the algorithm..."); ExperimentDAO experimentDAO = createExperimentInTheDatabase(experimentDTO, endpoint); @@ -559,7 +558,7 @@ public class ExperimentService { * @return the response to be returned */ public ExperimentDTO runGalaxyWorkflow(ExperimentDTO experimentDTO, String endpoint) { - UserDAO user = userInfo.getUser(); + UserDAO user = activeUserService.getActiveUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Running a workflow..."); ExperimentDAO experimentDAO = createExperimentInTheDatabase(experimentDTO, endpoint); @@ -660,7 +659,7 @@ public class ExperimentService { * @return nothing, just updates the experiment */ public void updateWorkflowExperiment(ExperimentDAO experimentDAO, String endpoint) { - UserDAO user = userInfo.getUser(); + UserDAO user = activeUserService.getActiveUser(); if (experimentDAO == null) { Logging.LogUserAction(user.getUsername(), endpoint, "The experiment does not exist."); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 000000000..65ab563a9 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,84 @@ +# Configuration template for the portal running inside a Docker container + +# See http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +spring: + portal-datasource: + url: "jdbc:postgresql://127.0.0.1:5433/portal" + schema: "public" + username: "portal" + password: "portalpwd" + driver-class-name: org.postgresql.Driver + + data: + jpa: + repositories: + bootstrap-mode: default + jpa: + hibernate: + dialect: org.hibernate.dialect.PostgreSQL9Dialect + ddl-auto: validate + +# WEB FRONTEND +frontend: + loginUrl: "http://127.0.0.1/services/login/hbp" + redirectAfterLoginUrl: "http://127.0.0.1/" + redirectAfterLogoutUrl: "http://127.0.0.1/services/login/hbp" + +logging: + level: + root: "DEBUG" + org: "DEBUG" + eu: + hbp: "DEBUG" + +# EMBEDDED SERVER CONFIGURATION +server: + servlet: + contextPath: "/services" + port: 8080 + forward-headers-strategy: native + session: + timeout: "2592000" + +# ENDPOINTS +endpoints: + enabled: true + health: + enabled: true + endpoint: "/health" + sensitive: false + +# External Services +services: + exareme: + queryExaremeUrl: "http://127.0.0.1:9090/mining/query" + algorithmsUrl: "http://127.0.0.1:9090/mining/algorithms.json" + + galaxy: + galaxyUrl: "http://127.0.0.1:8090" + galaxyContext: "nativeGalaxy/workflows/list" + galaxyApiKey: "d14a4cc5eebf805eb2ff261374ed08a2" + galaxyUsername: "admin" + galaxyPassword: "password" + +# Authentication +authentication: + enabled: true + +# Keycloak +keycloak: + enabled: true + auth-server-url: "http://127.0.0.1/auth" + realm: "MIP" + resource: "MIP" + enable-basic-auth: true + credentials: + secret: "dae83a6b-c769-4186-8383-f0984c6edf05" + principal-attribute: "preferred_username" + # cors: true + # cors-max-age: 3600 + # cors-allowed-methods: "GET, POST, PUT, PATCH, OPTIONS, DELETE" + # cors-allowed-headers: "*" + # cors-exposed-headers: "*" + + # logoutUrl: {{ .Env.LOGOUT_URL }} diff --git a/src/main/resources/test.test b/src/main/resources/test.test new file mode 100644 index 000000000..84be80128 --- /dev/null +++ b/src/main/resources/test.test @@ -0,0 +1,64 @@ +# Configuration template for the portal running inside a Docker container + +# See http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html + +spring.portal-datasource.url="jdbc:postgresql://127.0.0.1:5433/portal" +spring.portal-datasource.schema="public" +spring.portal-datasource.username="portal" +spring.portal-datasource.password="portalpwd" +spring.portal-datasource.driver-class-name=org.postgresql.Driver + +spring.data.jpa.repositories.bootstrap-mode=default +spring.jpa.hibernate.dialect=org.hibernate.dialect.PostgreSQL9Dialect +spring.jpa.hibernate.ddl-auto=validate + +# WEB FRONTEND +frontend.loginUrl="http://127.0.0.1/services/login/hbp" }} +frontend.redirectAfterLoginUrl="http://127.0.0.1/" +frontend.redirectAfterLogoutUrl="http://127.0.0.1/services/login/hbp" + +logging.level.root="DEBUG" +logging.level.org="DEBUG" +logging.level.eu.hbp="DEBUG" + +# EMBEDDED SERVER CONFIGURATION +server.servlet.contextPath="/services" +server.port=8080 +server.forward-headers-strategy=native +server.session.timeout="2592000" + +# ENDPOINTS +endpoints.enabled=true +endpoints.health.enabled: true +endpoints.health.endpoint: "/health" +endpoints.health.sensitive: false + +# External Services +services.exareme.queryExaremeUrl="http://localhost:9090/mining/query" +services.exareme.algorithmsUrl="http://localhost:9090/mining/algorithms.json" + + +galaxy.galaxyUrl="http://localhost:8090/" +galaxy.galaxyContext="nativeGalaxy/workflows/list" +galaxy.galaxyApiKey="sfas" +galaxy.galaxyUsername="admin" +galaxy.galaxyPassword="password" + +# Authentication +authentication.enabled="1" + +# Keycloak +keycloak.enabled=true +keycloak.auth-server-url="http://127.0.0.1/auth" +keycloak.realm="MIP" +keycloak.resource="MIP" +keycloak.enable-basic-auth=true +keycloak.credentials.secret="dae83a6b-c769-4186-8383-f0984c6edf05" +keycloak.principal-attribute="preferred_username" +# cors: true +# cors-max-age: 3600 +# cors-allowed-methods: "GET, POST, PUT, PATCH, OPTIONS, DELETE" +# cors-allowed-headers: "*" +# cors-exposed-headers: "*" + +# logoutUrl: {{ .Env.LOGOUT_URL }} -- GitLab From 1705e121ced29773bcd66b89a05c9fb3e0589d53 Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Thu, 12 Nov 2020 02:30:46 -0800 Subject: [PATCH 21/52] Added custom application.yml for non docker deployment. --- pom.xml | 1 + .../PersistenceConfiguration.java | 1 - src/main/resources/application.conf | 20 ----- src/main/resources/application.yml | 75 +++++++++++++++++++ src/main/resources/kamon.conf | 75 ------------------- 5 files changed, 76 insertions(+), 96 deletions(-) delete mode 100644 src/main/resources/application.conf create mode 100644 src/main/resources/application.yml delete mode 100644 src/main/resources/kamon.conf diff --git a/pom.xml b/pom.xml index e08146ef2..f9c84d85d 100644 --- a/pom.xml +++ b/pom.xml @@ -265,6 +265,7 @@ <include>**/*.csv</include> <include>**/*.sql</include> <include>**/*.conf</include> + <include>**/*.yml</include> <!-- Only for development --> </includes> <filtering>true</filtering> </resource> diff --git a/src/main/java/eu/hbp/mip/configuration/PersistenceConfiguration.java b/src/main/java/eu/hbp/mip/configuration/PersistenceConfiguration.java index bca25598a..909d2c1c8 100644 --- a/src/main/java/eu/hbp/mip/configuration/PersistenceConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/PersistenceConfiguration.java @@ -29,7 +29,6 @@ public class PersistenceConfiguration { return DataSourceBuilder.create().build(); } - @Bean(name = "entityManagerFactory") @DependsOn("flyway") public LocalContainerEntityManagerFactoryBean entityManagerFactory() { diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf deleted file mode 100644 index e2abfd5ae..000000000 --- a/src/main/resources/application.conf +++ /dev/null @@ -1,20 +0,0 @@ -app { - # Name of the application - name = "Portal backend" - # Type of the application - type = "Spring" - clusterSystemName = ${clustering.cluster.name} -} - -clustering { - ip = "127.0.0.1" - ip = ${?CLUSTER_IP} - port = 4489 - port = ${?CLUSTER_PORT} -} - -akka { - cluster { - roles = ["portal"] - } -} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 000000000..825692ef7 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,75 @@ +# Configuration template for the portal running inside a Docker container + +# See http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +spring: + portal-datasource: + url: "jdbc:postgresql://127.0.0.1:5433/portal" + schema: "public" + username: "portal" + password: "portalpwd" + driver-class-name: org.postgresql.Driver + + data: + jpa: + repositories: + bootstrap-mode: default + jpa: + hibernate: + dialect: org.hibernate.dialect.PostgreSQL9Dialect + ddl-auto: validate + +# HBP OAUTH2 LOGIN +hbp: + authentication: + enabled: true + client: + clientId: "MIP" + clientSecret: "dae83a6b-c769-4186-8383-f0984c6edf05" + logoutUri: http://127.0.0.1/auth/realms/MIP/protocol/openid-connect/logout + + +# WEB FRONTEND +frontend: + loginUrl: "http://127.0.0.1/services/login/hbp" + redirectAfterLoginUrl: "http://127.0.0.1/" + redirectAfterLogoutUrl: "http://127.0.0.1/services/login/hbp" + +logging: + level: + root: "DEBUG" + org: "DEBUG" + eu: + hbp: "DEBUG" + +# EMBEDDED SERVER CONFIGURATION +server: + servlet: + contextPath: "/services" + port: 8080 + forward-headers-strategy: native + session: + timeout: "2592000" + +# ENDPOINTS +endpoints: + enabled: true + health: + enabled: true + endpoint: "/health" + sensitive: false + +# External Services +services: + exareme: + queryExaremeUrl: "http://127.0.0.1:9090/mining/query" + algorithmsUrl: "http://127.0.0.1:9090/mining/algorithms.json" + + galaxy: + galaxyUrl: "http://127.0.0.1:8090" + galaxyContext: "nativeGalaxy/workflows/list" + galaxyApiKey: "d14a4cc5eebf805eb2ff261374ed08a2" + galaxyUsername: "admin" + galaxyPassword: "password" + + keycloak: + keycloakUrl: 127.0.0.1 \ No newline at end of file diff --git a/src/main/resources/kamon.conf b/src/main/resources/kamon.conf deleted file mode 100644 index face394cb..000000000 --- a/src/main/resources/kamon.conf +++ /dev/null @@ -1,75 +0,0 @@ -kamon { - enabled = no - enabled = ${?KAMON_ENABLED} - - environment { - service = "portal-backend" - service = ${?KAMON_SERVICE_NAME} - } - - zipkin = { - enabled = no - enabled = ${?ZIPKIN_ENABLED} - host = "zipkin" - host = ${?ZIPKIN_IP} - port = 9411 - port = ${?ZIPKIN_PORT} - } - - prometheus = { - enabled = no - enabled = ${?PROMETHEUS_ENABLED} - host = "prometheus" - host = ${?PROMETHEUS_IP} - port = 9090 - port = ${?PROMETHEUS_PORT} - } - - util.filters { - "akka.tracked-actor" { - includes = ["**"] - } - - "akka.tracked-dispatcher" { - includes = ["**"] - } - - "akka.traced-actor" { - includes = ["**"] - excludes = [ - ${clustering.cluster.name}"/system/IO**", - ${clustering.cluster.name}"/user/Stream**", - ${clustering.cluster.name}"/system/transports**", - ${clustering.cluster.name}"/system/cluster**", - ${clustering.cluster.name}"/system/remote**", - ${clustering.cluster.name}"/system/endpointmanager/**", - ${clustering.cluster.name}"/system/sharding/UserActor/**"] - } - - } - - akka-http { - add-http-status-code-as-metric-tag = true - } - - akka { - ask-pattern-timeout-warning = lightweight - } - - trace { - join-remote-parents-with-same-span-id = yes - sampler = "always" - } - - system-metrics { - host { - enabled = no - enabled = ${?SIGAR_SYSTEM_METRICS} - } - jvm { - enabled = no - enabled = ${?JVM_SYSTEM_METRICS} - } - } - -} -- GitLab From 94dffcf023d31b2a324e5725261e1f4032d39905 Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Thu, 12 Nov 2020 06:09:05 -0800 Subject: [PATCH 22/52] Added global variables for pathologies url and disabledAlgorithms. --- docker/config/application.tmpl | 6 ++++++ pom.xml | 8 ++++++++ .../configuration/SecurityConfiguration.java | 2 ++ .../eu/hbp/mip/controllers/AlgorithmsApi.java | 7 ++++++- .../hbp/mip/controllers/PathologiesApi.java | 19 +++++++++++++------ src/main/resources/application.yml | 12 +++++++++--- 6 files changed, 44 insertions(+), 10 deletions(-) diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index 6588e210a..f25878893 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -76,3 +76,9 @@ services: keycloak: keycloakUrl: {{ .Env.KEYCLOAK_URL }} + +pathologies: + pathologiesUrl: "file:/opt/portal/api/pathologies.json" + +algorithms: + disabledAlgorithmsUrl: "file:/opt/portal/api/disabledAlgorithms.json" \ No newline at end of file diff --git a/pom.xml b/pom.xml index f9c84d85d..b64596e79 100644 --- a/pom.xml +++ b/pom.xml @@ -268,6 +268,14 @@ <include>**/*.yml</include> <!-- Only for development --> </includes> <filtering>true</filtering> + + </resource> + <resource> + <directory>config</directory> + <includes> + <include>*.json</include> + </includes> + <filtering>true</filtering> </resource> </resources> diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index ff28602a1..81afa6418 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -6,6 +6,7 @@ import eu.hbp.mip.utils.CustomLoginUrlAuthenticationEntryPoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.security.oauth2.resource.AuthoritiesExtractor; import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; @@ -70,6 +71,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfiguration.class); + @Qualifier("oauth2ClientContext") @Autowired private OAuth2ClientContext oauth2ClientContext; diff --git a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java index d5dca19ae..07ab6ef29 100644 --- a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java +++ b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java @@ -17,6 +17,7 @@ import eu.hbp.mip.utils.Logging; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; import org.springframework.http.ResponseEntity; @@ -41,6 +42,7 @@ public class AlgorithmsApi { private static final Gson gson = new Gson(); + @Qualifier("userInfo") @Autowired private UserInfo userInfo; @@ -53,6 +55,9 @@ public class AlgorithmsApi { @Value("#{'${services.galaxy.galaxyApiKey}'}") private String galaxyApiKey; + @Value("#{'${services.algorithms.disabledAlgorithmsUrl}'}") + private String disabledAlgorithmsUrl; + @ApiOperation(value = "List all algorithms", response = String.class) @RequestMapping(method = RequestMethod.GET) public ResponseEntity<List<AlgorithmDTO>> getAlgorithms() { @@ -195,7 +200,7 @@ public class AlgorithmsApi { */ List<String> getDisabledAlgorithms() throws IOException { - Resource resource = resourceLoader.getResource("file:/opt/portal/api/disabledAlgorithms.json"); + Resource resource = resourceLoader.getResource(disabledAlgorithmsUrl); List<String> response = gson.fromJson(convertInputStreamToString( resource.getInputStream()), diff --git a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java index 4ec7c8e36..a6979e70c 100644 --- a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java +++ b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java @@ -15,6 +15,7 @@ import eu.hbp.mip.utils.InputStreamConverter; import eu.hbp.mip.utils.Logging; import io.swagger.annotations.Api; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; import org.springframework.http.ResponseEntity; @@ -35,6 +36,7 @@ public class PathologiesApi { private static final Gson gson = new Gson(); + @Qualifier("userInfo") @Autowired private UserInfo userInfo; @@ -42,32 +44,37 @@ public class PathologiesApi { @Value("#{'${hbp.authentication.enabled:1}'}") private boolean authenticationIsEnabled; + @Value("#{'${services.pathologies.pathologiesUrl}'}") + private String pathologiesUrl; + @Autowired private CustomResourceLoader resourceLoader; @RequestMapping(name = "/pathologies", method = RequestMethod.GET) public ResponseEntity<String> getPathologies(Authentication authentication) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /pathologies", "Loading pathologies ..."); + String endpoint = "(GET) /pathologies"; + String username = userInfo.getUser().getUsername(); + Logging.LogUserAction(username, endpoint, "Loading pathologies ..."); // Load pathologies from file - Resource resource = resourceLoader.getResource("file:/opt/portal/api/pathologies.json"); + Resource resource = resourceLoader.getResource(pathologiesUrl); List<PathologyDTO> allPathologies; try { allPathologies = gson.fromJson(InputStreamConverter.convertInputStreamToString(resource.getInputStream()), new TypeToken<List<PathologyDTO>>() { }.getType()); } catch (IOException e) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /pathologies", "Unable to load pathologies"); + Logging.LogUserAction(username, endpoint, "Unable to load pathologies"); throw new BadRequestException("The pathologies could not be loaded."); } // If authentication is disabled return everything if (!authenticationIsEnabled) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /pathologies", "Successfully loaded " + allPathologies.size() + " pathologies"); + Logging.LogUserAction(username, endpoint, "Successfully loaded " + allPathologies.size() + " pathologies"); return ResponseEntity.ok().body(gson.toJson(allPathologies)); } - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /pathologies", "Successfully loaded all authorized pathologies"); + Logging.LogUserAction(username, endpoint, "Successfully loaded all authorized pathologies"); return ResponseEntity.ok().body(ClaimUtils.getAuthorizedPathologies( - userInfo.getUser().getUsername(), authentication.getAuthorities(), allPathologies)); + username, authentication.getAuthorities(), allPathologies)); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 825692ef7..a4c205725 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -21,7 +21,7 @@ spring: # HBP OAUTH2 LOGIN hbp: authentication: - enabled: true + enabled: 0 client: clientId: "MIP" clientSecret: "dae83a6b-c769-4186-8383-f0984c6edf05" @@ -71,5 +71,11 @@ services: galaxyUsername: "admin" galaxyPassword: "password" - keycloak: - keycloakUrl: 127.0.0.1 \ No newline at end of file + keycloak: + keycloakUrl: "127.0.0.1" + + pathologies: + pathologiesUrl: "classPath:/pathologies.json" + + algorithms: + disabledAlgorithmsUrl: "classPath:/disableAlgorithms.json" \ No newline at end of file -- GitLab From 8a65e51898479dd25a0cc538d4c464e1441c72e4 Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Thu, 12 Nov 2020 07:47:14 -0800 Subject: [PATCH 23/52] Fixed dependencies that blocked the GET /algorithms. --- pom.xml | 1 - .../eu/hbp/mip/controllers/AlgorithmsApi.java | 39 +++++++++++-------- src/main/resources/application.yml | 4 +- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/pom.xml b/pom.xml index b64596e79..0c3f8a8c2 100644 --- a/pom.xml +++ b/pom.xml @@ -244,7 +244,6 @@ <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>logging-interceptor</artifactId> - <version>4.10.0-RC1</version> </dependency> <dependency> diff --git a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java index 07ab6ef29..8fc9fcbf9 100644 --- a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java +++ b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java @@ -61,24 +61,26 @@ public class AlgorithmsApi { @ApiOperation(value = "List all algorithms", response = String.class) @RequestMapping(method = RequestMethod.GET) public ResponseEntity<List<AlgorithmDTO>> getAlgorithms() { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "Executing..."); + String username = userInfo.getUser().getUsername(); + String endpoint = "(GET) /algorithms"; + Logging.LogUserAction(username, endpoint, "Executing..."); LinkedList<AlgorithmDTO> exaremeAlgorithms = getExaremeAlgorithms(); - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "Loaded " + exaremeAlgorithms.size() + " exareme algorithms"); + Logging.LogUserAction(username, endpoint, "Loaded " + exaremeAlgorithms.size() + " exareme algorithms"); LinkedList<AlgorithmDTO> galaxyAlgorithms = getGalaxyWorkflows(); - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "Loaded " + galaxyAlgorithms.size() + " galaxy algorithms"); + Logging.LogUserAction(username, endpoint, "Loaded " + galaxyAlgorithms.size() + " galaxy algorithms"); LinkedList<AlgorithmDTO> algorithms = new LinkedList<>(); if (exaremeAlgorithms != null) { algorithms.addAll(exaremeAlgorithms); } else { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", + Logging.LogUserAction(username, endpoint, "Getting exareme algorithms failed and returned null"); } if (galaxyAlgorithms != null) { algorithms.addAll(galaxyAlgorithms); } else { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", + Logging.LogUserAction(username, endpoint, "Getting galaxy workflows failed and returned null"); } @@ -86,7 +88,7 @@ public class AlgorithmsApi { try { disabledAlgorithms = getDisabledAlgorithms(); } catch (IOException e) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", + Logging.LogUserAction(username, endpoint, "The disabled algorithms could not be loaded."); } @@ -97,7 +99,7 @@ public class AlgorithmsApi { allowedAlgorithms.add(algorithm); } } - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", + Logging.LogUserAction(username, endpoint, "Successfully listed " + allowedAlgorithms.size() + " algorithms"); return ResponseEntity.ok(allowedAlgorithms); } @@ -108,6 +110,8 @@ public class AlgorithmsApi { * @return a list of AlgorithmDTOs or null if something fails */ public LinkedList<AlgorithmDTO> getExaremeAlgorithms() { + String username = userInfo.getUser().getUsername(); + String endpoint = "(GET) /algorithms"; LinkedList<AlgorithmDTO> algorithms = new LinkedList<>(); // Get exareme algorithms try { @@ -120,11 +124,11 @@ public class AlgorithmsApi { }.getType() ); } catch (IOException e) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "An exception occurred: " + e.getMessage()); + Logging.LogUserAction(username, endpoint, "An exception occurred: " + e.getMessage()); return null; } - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", + Logging.LogUserAction(username, endpoint, "Completed, returned " + algorithms.size() + " algorithms."); return algorithms; } @@ -135,7 +139,8 @@ public class AlgorithmsApi { * @return a list of AlgorithmDTOs or null if something fails */ public LinkedList<AlgorithmDTO> getGalaxyWorkflows() { - + String username = userInfo.getUser().getUsername(); + String endpoint = "(GET) /algorithms"; List<Workflow> workflowList; try { // Get all the workflows with the galaxy client @@ -144,7 +149,7 @@ public class AlgorithmsApi { workflowList = new ArrayList<>(workflowsClient.getWorkflows()); } catch (Exception e) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "Error when calling list galaxy workflows: " + e.getMessage()); + Logging.LogUserAction(username, endpoint, "Error when calling list galaxy workflows: " + e.getMessage()); return null; } @@ -164,28 +169,28 @@ public class AlgorithmsApi { } else { // Something unexpected happened String msgErr = gson.toJson(response.errorBody()); - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "Error Response: " + msgErr); + Logging.LogUserAction(username, endpoint, "Error Response: " + msgErr); return null; } } catch (Exception e) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "An exception occurred: " + e.getMessage()); + Logging.LogUserAction(username, endpoint, "An exception occurred: " + e.getMessage()); return null; } } - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "Workflows fetched: " + workflows.size()); + Logging.LogUserAction(username, endpoint, "Workflows fetched: " + workflows.size()); // Convert the workflows to algorithms LinkedList<AlgorithmDTO> algorithms = new LinkedList<>(); for (WorkflowDTO workflow : workflows) { - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "Converting workflow: " + workflow); + Logging.LogUserAction(username, endpoint, "Converting workflow: " + workflow); algorithms.add(workflow.convertToAlgorithmDTO()); - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", + Logging.LogUserAction(username, endpoint, "Converted algorithm: " + algorithms.get(algorithms.size() - 1)); } - Logging.LogUserAction(userInfo.getUser().getUsername(), "(GET) /algorithms", "Completed!"); + Logging.LogUserAction(username, endpoint, "Completed!"); return algorithms; } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a4c205725..1728999c2 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -36,8 +36,8 @@ frontend: logging: level: - root: "DEBUG" - org: "DEBUG" + root: "ERROR" + org: "ERROR" eu: hbp: "DEBUG" -- GitLab From d1c2859496b44517c61768658496d10fdfade326 Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Fri, 13 Nov 2020 03:57:25 -0800 Subject: [PATCH 24/52] Beautification of non_docker additions. --- docker/config/application.tmpl | 9 ++++----- pom.xml | 3 ++- .../java/eu/hbp/mip/controllers/AlgorithmsApi.java | 6 +++--- .../java/eu/hbp/mip/controllers/PathologiesApi.java | 10 +++------- src/main/java/eu/hbp/mip/controllers/SecurityApi.java | 2 +- src/main/resources/application.yml | 9 ++++----- 6 files changed, 17 insertions(+), 22 deletions(-) diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index f25878893..5bdfa36a1 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -77,8 +77,7 @@ services: keycloak: keycloakUrl: {{ .Env.KEYCLOAK_URL }} -pathologies: - pathologiesUrl: "file:/opt/portal/api/pathologies.json" - -algorithms: - disabledAlgorithmsUrl: "file:/opt/portal/api/disabledAlgorithms.json" \ No newline at end of file +# Files are imported when building the docker image +files: + pathologies_json: "file:/opt/portal/api/pathologies.json" + disabledAlgorithms_json: "file:/opt/portal/api/disabledAlgorithms.json" \ No newline at end of file diff --git a/pom.xml b/pom.xml index b64596e79..ba87e5423 100644 --- a/pom.xml +++ b/pom.xml @@ -268,8 +268,9 @@ <include>**/*.yml</include> <!-- Only for development --> </includes> <filtering>true</filtering> - </resource> + + <!-- Used for development. Docker/config folder files added as resources --> <resource> <directory>config</directory> <includes> diff --git a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java index 07ab6ef29..e2c3b6e0e 100644 --- a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java +++ b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java @@ -55,8 +55,8 @@ public class AlgorithmsApi { @Value("#{'${services.galaxy.galaxyApiKey}'}") private String galaxyApiKey; - @Value("#{'${services.algorithms.disabledAlgorithmsUrl}'}") - private String disabledAlgorithmsUrl; + @Value("#{'${files.disabledAlgorithms_json}'}") + private String disabledAlgorithmsFilePath; @ApiOperation(value = "List all algorithms", response = String.class) @RequestMapping(method = RequestMethod.GET) @@ -200,7 +200,7 @@ public class AlgorithmsApi { */ List<String> getDisabledAlgorithms() throws IOException { - Resource resource = resourceLoader.getResource(disabledAlgorithmsUrl); + Resource resource = resourceLoader.getResource(disabledAlgorithmsFilePath); List<String> response = gson.fromJson(convertInputStreamToString( resource.getInputStream()), diff --git a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java index a6979e70c..383515660 100644 --- a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java +++ b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java @@ -1,7 +1,3 @@ -/** - * Created by mirco on 04.12.15. - */ - package eu.hbp.mip.controllers; import com.google.gson.Gson; @@ -44,8 +40,8 @@ public class PathologiesApi { @Value("#{'${hbp.authentication.enabled:1}'}") private boolean authenticationIsEnabled; - @Value("#{'${services.pathologies.pathologiesUrl}'}") - private String pathologiesUrl; + @Value("#{'${files.pathologies_json}'}") + private String pathologiesFilePath; @Autowired private CustomResourceLoader resourceLoader; @@ -57,7 +53,7 @@ public class PathologiesApi { Logging.LogUserAction(username, endpoint, "Loading pathologies ..."); // Load pathologies from file - Resource resource = resourceLoader.getResource(pathologiesUrl); + Resource resource = resourceLoader.getResource(pathologiesFilePath); List<PathologyDTO> allPathologies; try { allPathologies = gson.fromJson(InputStreamConverter.convertInputStreamToString(resource.getInputStream()), new TypeToken<List<PathologyDTO>>() { diff --git a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java index e179b3172..77b22fae8 100644 --- a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java +++ b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java @@ -96,7 +96,7 @@ public class SecurityApi { @Value("#{'${services.galaxy.galaxyPassword:password}'}") private String galaxyPassword; - @Value("#{'${services.galaxy.galaxyContext:nativeGalaxy}'}") + @Value("#{'${services.galaxy.galaxpathoyContext:nativeGalaxy}'}") private String galaxyContext; /** diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a4c205725..5fda7331d 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -74,8 +74,7 @@ services: keycloak: keycloakUrl: "127.0.0.1" - pathologies: - pathologiesUrl: "classPath:/pathologies.json" - - algorithms: - disabledAlgorithmsUrl: "classPath:/disableAlgorithms.json" \ No newline at end of file +# Files are loaded from the resources +files: + pathologies_json: "classPath:/pathologies.json" + disabledAlgorithms_json: "classPath:/disabledAlgorithms.json" \ No newline at end of file -- GitLab From dd3ac2f9a0585f923a398c70bcd56569a81c130c Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Tue, 17 Nov 2020 07:14:54 -0800 Subject: [PATCH 25/52] Removing users endpoint and security cleanup. --- pom.xml | 14 ---- .../configuration/SecurityConfiguration.java | 12 +++- .../SecurityUtils/CORSFilter.java | 1 + .../eu/hbp/mip/controllers/SecurityApi.java | 2 +- .../java/eu/hbp/mip/controllers/UsersApi.java | 29 ++------- src/main/resources/application.yml | 7 -- src/main/resources/test.test | 64 ------------------- 7 files changed, 18 insertions(+), 111 deletions(-) delete mode 100644 src/main/resources/test.test diff --git a/pom.xml b/pom.xml index 1019b1ccf..2be66e4cd 100644 --- a/pom.xml +++ b/pom.xml @@ -96,20 +96,6 @@ <artifactId>keycloak-spring-security-adapter</artifactId> <version>${keycloak-spring.version}</version> </dependency> -<!-- <dependency>--> -<!-- <groupId>org.springframework.security</groupId>--> -<!-- <artifactId>spring-security-oauth2-resource-server</artifactId>--> -<!-- </dependency>--> -<!-- <dependency>--> -<!-- <groupId>org.springframework.security.oauth.boot</groupId>--> -<!-- <artifactId>spring-security-oauth2-autoconfigure</artifactId>--> -<!-- <version>2.0.1.RELEASE</version>--> -<!-- </dependency>--> -<!-- <dependency>--> -<!-- <groupId>org.springframework.security.oauth</groupId>--> -<!-- <artifactId>spring-security-oauth2</artifactId>--> -<!-- <version>2.5.0.RELEASE</version>--> -<!-- </dependency>--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index 8617e2270..8ebf2c376 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -22,6 +22,9 @@ import org.springframework.security.web.authentication.session.SessionAuthentica import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.csrf.CsrfTokenRepository; import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.WebUtils; @@ -31,6 +34,8 @@ import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; // Reference for OAuth2 login: https://spring.io/guides/tutorials/spring-boot-oauth2/ @@ -104,9 +109,9 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter super.configure(http); //disableCertificateValidation(); // TODO needed? - // TODO Check if needed. - // Check if it works when removing keycloak cors from app properties. - http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class); + // TODO Is that needed for development? On Galaxy? +// http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class); +// http.cors(); if (authenticationEnabled) { http.antMatcher("/**") @@ -138,6 +143,7 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter } } + // @Bean // public FilterRegistrationBean corsFilter() { // UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CORSFilter.java b/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CORSFilter.java index 2a259ea22..b7d3f7733 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CORSFilter.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CORSFilter.java @@ -8,6 +8,7 @@ import java.io.IOException; * Created by mirco on 12.02.16. */ public class CORSFilter implements Filter { + // TODO needed? @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { diff --git a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java index 7bc4fb7b7..9ee70b0be 100644 --- a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java +++ b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java @@ -31,7 +31,7 @@ public class SecurityApi { @Autowired private SecurityConfiguration securityConfiguration; - // TODO How to redirect? keycloak off? + // TODO Fix no authentication instance @RequestMapping(path = "/login/hbp", method = RequestMethod.GET) @ConditionalOnExpression("${authentication.enabled:0}") public void noLogin(HttpServletResponse httpServletResponse) throws IOException { diff --git a/src/main/java/eu/hbp/mip/controllers/UsersApi.java b/src/main/java/eu/hbp/mip/controllers/UsersApi.java index 66f34b670..1c60aba1e 100644 --- a/src/main/java/eu/hbp/mip/controllers/UsersApi.java +++ b/src/main/java/eu/hbp/mip/controllers/UsersApi.java @@ -21,32 +21,17 @@ import java.net.URLEncoder; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @RestController -@RequestMapping(value = "/users", produces = {APPLICATION_JSON_VALUE}) -@Api(value = "/users") +@RequestMapping(value = "/activeUser", produces = {APPLICATION_JSON_VALUE}) +@Api(value = "/activeUser") public class UsersApi { @Autowired private ActiveUserService activeUserService; - @Autowired - private UserRepository userRepository; - - @ApiOperation(value = "Get a user", response = UserDAO.class) - @RequestMapping(value = "/{username}", method = RequestMethod.GET) - public ResponseEntity<UserDAO> getAUser( - @ApiParam(value = "username", required = true) @PathVariable("username") String username - ) { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /users/{username}", - "Loaded a user with username : " + username); - - // TODO Error handling? - return ResponseEntity.ok(userRepository.findByUsername(username)); - } - @ApiOperation(value = "Get the active user", response = UserDAO.class) - @RequestMapping(value = "/activeUser", method = RequestMethod.GET) + @RequestMapping(method = RequestMethod.GET) public ResponseEntity<UserDAO> getTheActiveUser(HttpServletResponse response) { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /users/activeUser", + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /activeUser", "Loading the details of the activeUser"); UserDAO activeUser = activeUserService.getActiveUser(); @@ -62,7 +47,7 @@ public class UsersApi { response.addCookie(cookie); } catch (JsonProcessingException | UnsupportedEncodingException e) { Logging.LogUserAction(activeUser.getUsername(), - "(GET) /users/activeUser", "Failed to add Cookie. Exception: " + e.getMessage()); + "(GET) /activeUser", "Failed to add Cookie. Exception: " + e.getMessage()); } return ResponseEntity.ok(activeUserService.getActiveUser()); @@ -70,9 +55,9 @@ public class UsersApi { // TODO Kostas, why not working? @ApiOperation(value = "The active user agrees to the NDA", response = UserDAO.class) - @RequestMapping(value = "/activeUser/agreeNDA", method = RequestMethod.POST) + @RequestMapping(value = "/agreeNDA", method = RequestMethod.POST) public ResponseEntity<UserDAO> activeUserServiceAgreesToNDA(@RequestBody(required = false) UserDAO userDAO) { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /users/activeUser/agreeNDA", + Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /activeUser/agreeNDA", "The user agreed to the NDA"); return ResponseEntity.ok(activeUserService.agreeToNDA()); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index bd45b740e..7ff94d087 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -37,8 +37,6 @@ server: contextPath: "/services" port: 8080 forward-headers-strategy: native - session: - timeout: "2592000" # ENDPOINTS endpoints: @@ -75,11 +73,6 @@ keycloak: credentials: secret: "dae83a6b-c769-4186-8383-f0984c6edf05" principal-attribute: "preferred_username" - # cors: true - # cors-max-age: 3600 - # cors-allowed-methods: "GET, POST, PUT, PATCH, OPTIONS, DELETE" - # cors-allowed-headers: "*" - # cors-exposed-headers: "*" # logoutUrl: {{ .Env.LOGOUT_URL }} diff --git a/src/main/resources/test.test b/src/main/resources/test.test deleted file mode 100644 index 84be80128..000000000 --- a/src/main/resources/test.test +++ /dev/null @@ -1,64 +0,0 @@ -# Configuration template for the portal running inside a Docker container - -# See http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html - -spring.portal-datasource.url="jdbc:postgresql://127.0.0.1:5433/portal" -spring.portal-datasource.schema="public" -spring.portal-datasource.username="portal" -spring.portal-datasource.password="portalpwd" -spring.portal-datasource.driver-class-name=org.postgresql.Driver - -spring.data.jpa.repositories.bootstrap-mode=default -spring.jpa.hibernate.dialect=org.hibernate.dialect.PostgreSQL9Dialect -spring.jpa.hibernate.ddl-auto=validate - -# WEB FRONTEND -frontend.loginUrl="http://127.0.0.1/services/login/hbp" }} -frontend.redirectAfterLoginUrl="http://127.0.0.1/" -frontend.redirectAfterLogoutUrl="http://127.0.0.1/services/login/hbp" - -logging.level.root="DEBUG" -logging.level.org="DEBUG" -logging.level.eu.hbp="DEBUG" - -# EMBEDDED SERVER CONFIGURATION -server.servlet.contextPath="/services" -server.port=8080 -server.forward-headers-strategy=native -server.session.timeout="2592000" - -# ENDPOINTS -endpoints.enabled=true -endpoints.health.enabled: true -endpoints.health.endpoint: "/health" -endpoints.health.sensitive: false - -# External Services -services.exareme.queryExaremeUrl="http://localhost:9090/mining/query" -services.exareme.algorithmsUrl="http://localhost:9090/mining/algorithms.json" - - -galaxy.galaxyUrl="http://localhost:8090/" -galaxy.galaxyContext="nativeGalaxy/workflows/list" -galaxy.galaxyApiKey="sfas" -galaxy.galaxyUsername="admin" -galaxy.galaxyPassword="password" - -# Authentication -authentication.enabled="1" - -# Keycloak -keycloak.enabled=true -keycloak.auth-server-url="http://127.0.0.1/auth" -keycloak.realm="MIP" -keycloak.resource="MIP" -keycloak.enable-basic-auth=true -keycloak.credentials.secret="dae83a6b-c769-4186-8383-f0984c6edf05" -keycloak.principal-attribute="preferred_username" -# cors: true -# cors-max-age: 3600 -# cors-allowed-methods: "GET, POST, PUT, PATCH, OPTIONS, DELETE" -# cors-allowed-headers: "*" -# cors-exposed-headers: "*" - -# logoutUrl: {{ .Env.LOGOUT_URL }} -- GitLab From 966db4c9e042214630eb3ce34fb51370f996c686 Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Tue, 17 Nov 2020 09:36:27 -0800 Subject: [PATCH 26/52] Keycloak security authentication/authorization fixed. --- docker/config/application.tmpl | 34 +-- .../mip/configuration/CacheConfiguration.java | 13 - .../GalaxyAuthentication.java} | 19 +- .../configuration/SecurityConfiguration.java | 288 ++---------------- .../SecurityUtils/CORSFilter.java | 23 -- .../CustomAccessDeniedHandler.java | 32 -- ...ustomLoginUrlAuthenticationEntryPoint.java | 20 -- .../mip/configuration/WebConfiguration.java | 5 - .../java/eu/hbp/mip/controllers/UsersApi.java | 3 +- .../hbp/mip/services/ActiveUserService.java | 8 +- .../java/eu/hbp/mip/utils/ClaimUtils.java | 4 +- src/main/resources/application.yml | 26 +- 12 files changed, 59 insertions(+), 416 deletions(-) delete mode 100644 src/main/java/eu/hbp/mip/configuration/CacheConfiguration.java rename src/main/java/eu/hbp/mip/{controllers/SecurityApi.java => configuration/GalaxyAuthentication.java} (74%) delete mode 100644 src/main/java/eu/hbp/mip/configuration/SecurityUtils/CORSFilter.java delete mode 100644 src/main/java/eu/hbp/mip/configuration/SecurityUtils/CustomAccessDeniedHandler.java delete mode 100644 src/main/java/eu/hbp/mip/configuration/SecurityUtils/CustomLoginUrlAuthenticationEntryPoint.java diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index d685d2f2f..257bfa087 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -1,6 +1,6 @@ # Configuration template for the portal running inside a Docker container -# See http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +### DATABASE CONFIGURATION ### spring: portal-datasource: url: {{ default .Env.PORTAL_DB_URL "jdbc:postgresql://88.197.53.106:5432/portal" }} @@ -8,7 +8,6 @@ spring: username: {{ default .Env.PORTAL_DB_USER "postgres" }} password: {{ .Env.PORTAL_DB_PASSWORD }} driver-class-name: org.postgresql.Driver - data: jpa: repositories: @@ -18,12 +17,7 @@ spring: dialect: org.hibernate.dialect.PostgreSQL9Dialect ddl-auto: validate -# WEB FRONTEND -frontend: - loginUrl: {{ default .Env.FRONTEND_LOGIN_URL "http://frontend/services/login/hbp" }} - redirectAfterLoginUrl: {{ default .Env.FRONTEND_AFTER_LOGIN_URL "http://frontend/" }} - redirectAfterLogoutUrl: {{ default .Env.FRONTEND_AFTER_LOGOUT_URL "http://frontend/services/login/hbp" }} - +### LOG LEVELS ### logging: level: root: {{ default .Env.LOG_LEVEL_FRAMEWORK "ERROR" }} @@ -31,16 +25,14 @@ logging: eu: hbp: {{ default .Env.LOG_LEVEL "INFO" }} -# EMBEDDED SERVER CONFIGURATION +### EMBEDDED SERVER CONFIGURATION ### server: servlet: - contextPath: {{ default .Env.CONTEXT_PATH "/services" }} + contextPath: "/services" port: 8080 - use-forward-headers: true - session: - timeout: {{ default .Env.SESSION_TIMEOUT "2592000" }} + forward-headers-strategy: native -# ENDPOINTS +### ENDPOINTS ### endpoints: enabled: true health: @@ -48,7 +40,7 @@ endpoints: endpoint: /health sensitive: false -# External Services +### EXTERNAL SERVICES ### services: exareme: queryExaremeUrl: {{ default .Env.EXAREME_URL "http://localhost:9090" }}/mining/query @@ -61,11 +53,11 @@ services: galaxyUsername: {{ default .Env.GALAXY_USERNAME "admin" }} galaxyPassword: {{ default .Env.GALAXY_PASSWORD "password" }} -# Authentication +### Authentication ### authentication: enabled: {{ default .Env.AUTHENTICATION "1" }} -# Keycloak +### Keycloak ### keycloak: enabled: true auth-server-url: {{ .Env.KEYCLOAK_AUTH_URL }} @@ -75,14 +67,8 @@ keycloak: credentials: secret: {{ .Env.CLIENT_SECRET }} principal-attribute: "preferred_username" -# cors: true -# cors-max-age: 3600 -# cors-allowed-methods: "GET, POST, PUT, PATCH, OPTIONS, DELETE" -# cors-allowed-headers: "*" -# cors-exposed-headers: "*" - - # logoutUrl: {{ .Env.LOGOUT_URL }} +### EXTERNAL FILES ### # Files are imported when building the docker image files: pathologies_json: "file:/opt/portal/api/pathologies.json" diff --git a/src/main/java/eu/hbp/mip/configuration/CacheConfiguration.java b/src/main/java/eu/hbp/mip/configuration/CacheConfiguration.java deleted file mode 100644 index b7300fc25..000000000 --- a/src/main/java/eu/hbp/mip/configuration/CacheConfiguration.java +++ /dev/null @@ -1,13 +0,0 @@ -package eu.hbp.mip.configuration; - -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.context.annotation.Configuration; - -/** - * Created by mirco on 07.11.16. - */ - -@Configuration -@EnableCaching -public class CacheConfiguration { -} diff --git a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java b/src/main/java/eu/hbp/mip/configuration/GalaxyAuthentication.java similarity index 74% rename from src/main/java/eu/hbp/mip/controllers/SecurityApi.java rename to src/main/java/eu/hbp/mip/configuration/GalaxyAuthentication.java index 9ee70b0be..7910e809f 100644 --- a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java +++ b/src/main/java/eu/hbp/mip/configuration/GalaxyAuthentication.java @@ -1,4 +1,4 @@ -package eu.hbp.mip.controllers; +package eu.hbp.mip.configuration; import com.google.gson.Gson; import com.google.gson.JsonObject; @@ -21,24 +21,11 @@ import java.io.IOException; import java.util.Base64; @RestController -public class SecurityApi { - - private static final Gson gson = new Gson(); +public class GalaxyAuthentication { @Autowired private ActiveUserService activeUserService; - @Autowired - private SecurityConfiguration securityConfiguration; - - // TODO Fix no authentication instance - @RequestMapping(path = "/login/hbp", method = RequestMethod.GET) - @ConditionalOnExpression("${authentication.enabled:0}") - public void noLogin(HttpServletResponse httpServletResponse) throws IOException { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /user/login/hbp", "Unauthorized login."); - httpServletResponse.sendRedirect(securityConfiguration.getFrontendRedirectAfterLogin()); - } - @Value("#{'${services.galaxy.galaxyUsername:admin}'}") private String galaxyUsername; @@ -63,6 +50,6 @@ public class SecurityApi { object.addProperty("context", galaxyContext); Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /user/galaxy", "Successfully Loaded galaxy information."); - return ResponseEntity.ok(gson.toJson(object)); + return ResponseEntity.ok(new Gson().toJson(object)); } } diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index 8ebf2c376..796f900a5 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -1,14 +1,10 @@ package eu.hbp.mip.configuration; -import eu.hbp.mip.configuration.SecurityUtils.CORSFilter; import org.keycloak.adapters.KeycloakConfigResolver; import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; import org.keycloak.adapters.springsecurity.KeycloakConfiguration; import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; -import org.keycloak.adapters.springsecurity.authentication.KeycloakLogoutHandler; import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -16,67 +12,54 @@ import org.springframework.security.config.annotation.authentication.builders.Au import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; import org.springframework.security.core.session.SessionRegistryImpl; -import org.springframework.security.web.access.channel.ChannelProcessingFilter; import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; -import org.springframework.security.web.csrf.CsrfToken; -import org.springframework.security.web.csrf.CsrfTokenRepository; -import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.filter.OncePerRequestFilter; -import org.springframework.web.util.WebUtils; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; -import javax.servlet.FilterChain; import javax.servlet.ServletException; -import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -// Reference for OAuth2 login: https://spring.io/guides/tutorials/spring-boot-oauth2/ -// also http://cscarioni.blogspot.ch/2013/04/pro-spring-security-and-oauth-2.html -// Security with Keycloak: https://www.thomasvitale.com/keycloak-authentication-flow-sso-client/ +// Keycloak security with keycloak-spring-boot-starter +@Controller @KeycloakConfiguration public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter { - private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfiguration.class); - - /** - * Enable HBP collab authentication (1) or disable it (0). Default is 1 - */ @Value("#{'${authentication.enabled}'}") private boolean authenticationEnabled; - /** - * Absolute URL to redirect to when login is required - */ - @Value("#{'${frontend.loginUrl}'}") - private String loginUrl; - - /** - * Absolute URL to redirect to after successful login - */ - @Value("#{'${frontend.redirectAfterLoginUrl}'}") - private String frontendRedirectAfterLogin; + // Redirect to login page url + private static final String logoutRedirectURL = "/sso/login"; - /** - * Absolute URL to redirect to after successful logout - */ - @Value("#{'${frontend.redirectAfterLogoutUrl}'}") - private String redirectAfterLogoutUrl; + @Override + protected void configure(HttpSecurity http) throws Exception { + super.configure(http); - public boolean getAuthenticationEnabled() { - return authenticationEnabled; + if (authenticationEnabled) { + http.authorizeRequests() + .antMatchers( + "/sso/login", "/webjars/**", + "/v2/api-docs", "/swagger-ui/**", "/swagger-resources/**" // Swagger URLs + ).permitAll() + .antMatchers("/galaxy*", "/galaxy/*").hasRole("DATA MANAGER") + .anyRequest().hasRole("RESEARCHER"); + } else { + http.antMatcher("/**") + .authorizeRequests() + .antMatchers("/**").permitAll() + .and().csrf().disable(); + } } - public String getFrontendRedirectAfterLogin() { - return frontendRedirectAfterLogin; + @Autowired + private HttpServletRequest request; + + @GetMapping(value = "/logout") + public String logout() throws ServletException { + request.logout(); + return "redirect:" + logoutRedirectURL; } @Bean @@ -99,215 +82,4 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter auth.authenticationProvider(keycloakAuthenticationProvider); } - @Override - protected KeycloakLogoutHandler keycloakLogoutHandler() throws Exception { - return super.keycloakLogoutHandler(); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - super.configure(http); - //disableCertificateValidation(); // TODO needed? - - // TODO Is that needed for development? On Galaxy? -// http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class); -// http.cors(); - - if (authenticationEnabled) { - http.antMatcher("/**") - .authorizeRequests() - .antMatchers("/**").permitAll() - .and().csrf().disable(); -// .antMatchers( -// "/login**", "/health/**", "/info/**", "/metrics/**", -// "/trace/**", "/frontend/**", "/webjars/**", "/v2/api-docs", -// "/swagger-ui.html", "/swagger-resources/**" -// ).permitAll() -// .antMatchers("/galaxy*", "/galaxy/*").hasRole("DATA MANAGER") -// .anyRequest().hasRole("RESEARCHER") - // .and().exceptionHandling().authenticationEntryPoint(new CustomLoginUrlAuthenticationEntryPoint(loginUrl)) - // .accessDeniedHandler(new CustomAccessDeniedHandler()) -// .and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository()) -// .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class).csrf(); -// .and().logout().logoutSuccessUrl("/logout"); -// -// -// // TODO .and().logout().addLogoutHandler(authLogoutHandler()).logoutSuccessUrl(redirectAfterLogoutUrl) -// .and().logout().permitAll() - // TODO ?? .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class); - } else { - http.antMatcher("/**") - .authorizeRequests() - .antMatchers("/**").permitAll() - .and().csrf().disable(); - } - } - - -// @Bean -// public FilterRegistrationBean corsFilter() { -// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); -// CorsConfiguration config = new CorsConfiguration(); -// config.setAllowCredentials(true); -// config.addAllowedOrigin("*"); -// config.addAllowedHeader("*"); -// config.addAllowedMethod("*"); -// source.registerCorsConfiguration("/**", config); -// -// FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); -// bean.setOrder(-100); -// return bean; -// } - -// private Filter ssoFilter() { -// OAuth2ClientAuthenticationProcessingFilter hbpFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/hbp"); -// OAuth2RestTemplate hbpTemplate = new OAuth2RestTemplate(hbp(), oauth2ClientContext); -// hbpFilter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(frontendRedirectAfterLogin)); -// hbpFilter.setRestTemplate(hbpTemplate); -// hbpFilter.setTokenServices(new activeUserServiceTokenServices(hbpResource().getactiveUserServiceUri(), hbp().getClientId())); -// return hbpFilter; -// } - -// @Bean -// public FilterRegistrationBean oauth2ClientFilterRegistration( -// OAuth2ClientContextFilter filter) { -// FilterRegistrationBean registration = new FilterRegistrationBean(); -// registration.setFilter(filter); -// registration.setOrder(-100); -// return registration; -// } - -// @Bean(name = "hbp") -// @ConfigurationProperties("hbp.client") -// public BaseOAuth2ProtectedResourceDetails hbp() { -// return new AuthorizationCodeResourceDetails(); -// } -// -// @Bean(name = "hbpResource") -// @ConfigurationProperties("hbp.resource") -// public ResourceServerProperties hbpResource() { -// return new ResourceServerProperties(); -// } - - - private OncePerRequestFilter csrfHeaderFilter() { - return new OncePerRequestFilter() { - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, - FilterChain filterChain) throws ServletException, IOException { - CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); - if (csrf != null) { - Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN"); - String token = csrf.getToken(); - if (cookie == null || token != null && !token.equals(cookie.getValue())) { - cookie = new Cookie("XSRF-TOKEN", token); - cookie.setPath("/"); - response.addCookie(cookie); - } - } - filterChain.doFilter(request, response); - } - }; - } - - private CsrfTokenRepository csrfTokenRepository() { - HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository(); - repository.setHeaderName("X-XSRF-TOKEN"); - return repository; - } - -// @Bean -// public AuthoritiesExtractor keycloakAuthoritiesExtractor() { -// return new KeycloakAuthoritiesExtractor(); -// } -// -// -// public class KeycloakAuthoritiesExtractor -// implements AuthoritiesExtractor { -// -// @Override -// public List<GrantedAuthority> extractAuthorities -// (Map<String, Object> map) { -// return AuthorityUtils -// .commaSeparatedStringToAuthorityList(asAuthorities(map)); -// } -// -// private String asAuthorities(Map<String, Object> map) { -// List<String> authorities = new ArrayList<>(); -//// authorities.add("BAELDUNG_USER"); -// List<LinkedHashMap<String, String>> authz; -// authz = (List<LinkedHashMap<String, String>>) map.get("authorities"); -// for (LinkedHashMap<String, String> entry : authz) { -// authorities.add(entry.get("authority")); -// } -// return String.join(",", authorities); -// } -// } - - -// private LogoutHandler authLogoutHandler() { -// return (request, response, authentication) -> { -// logout(); -// }; -// } - - -// public void logout() { -// // TODO Try removing -// -// RestTemplate restTemplate = new RestTemplate(); -// MultiValueMap<String, String> formParams = new LinkedMultiValueMap<>(); -// formParams.add("client_id", hbp().getClientId()); -// formParams.add("client_secret", hbp().getClientSecret()); -// formParams.add("refresh_token", this.oauth2ClientContext.getAccessToken().getRefreshToken().getValue()); -// -// HttpHeaders httpHeaders = new HttpHeaders(); -// httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE); -// RequestEntity<MultiValueMap<String, String>> requestEntity = -// new RequestEntity<>(formParams, httpHeaders, HttpMethod.POST, -// URI.create(logoutUrl)); -// restTemplate.exchange(requestEntity, String.class); -// } - - // TODO needed? -// @Value("#{'${keycloak.auth-server-url}'}") -// private String keycloakUrl; -// -// public void disableCertificateValidation() { -// -// //TODO Refactor logging -// -// LOGGER.info("disabling certificate validation host : " + keycloakUrl); -// -// // Create a trust manager that does not validate certificate chains -// TrustManager[] trustAllCerts = new TrustManager[]{ -// new X509TrustManager() { -// public X509Certificate[] getAcceptedIssuers() { -// return new X509Certificate[0]; -// } -// -// public void checkClientTrusted(X509Certificate[] certs, String authType) { -// } -// -// public void checkServerTrusted(X509Certificate[] certs, String authType) { -// } -// }}; -// -// -// // Ignore differences between given hostname and certificate hostname -// HostnameVerifier hv = -// (hostname, session) -> hostname.equals(keycloakUrl) && session.getPeerHost().equals(keycloakUrl); -// -// // Install the all-trusting trust manager -// try { -// SSLContext sc = SSLContext.getInstance("SSL"); -// sc.init(null, trustAllCerts, new SecureRandom()); -// HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); -// HttpsURLConnection.setDefaultHostnameVerifier(hv); -// } catch (Exception e) { -// // TODO add log message -// } -// -// } - } diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CORSFilter.java b/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CORSFilter.java deleted file mode 100644 index b7d3f7733..000000000 --- a/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CORSFilter.java +++ /dev/null @@ -1,23 +0,0 @@ -package eu.hbp.mip.configuration.SecurityUtils; - -import javax.servlet.*; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * Created by mirco on 12.02.16. - */ -public class CORSFilter implements Filter { - // TODO needed? - - @Override - public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { - HttpServletResponse response = (HttpServletResponse) res; - response.setHeader("Access-Control-Allow-Origin", "*"); - response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT"); - response.setHeader("Access-Control-Max-Age", "3600"); - response.setHeader("Access-Control-Allow-Headers", "*"); - response.setHeader("Access-Control-Request-Headers", "*"); - chain.doFilter(req, res); - } -} diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CustomAccessDeniedHandler.java b/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CustomAccessDeniedHandler.java deleted file mode 100644 index 552abd29f..000000000 --- a/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CustomAccessDeniedHandler.java +++ /dev/null @@ -1,32 +0,0 @@ -package eu.hbp.mip.configuration.SecurityUtils; - -import org.codehaus.jettison.json.JSONException; -import org.codehaus.jettison.json.JSONObject; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.web.access.AccessDeniedHandler; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.sql.Timestamp; - -public class CustomAccessDeniedHandler implements AccessDeniedHandler { - @Override - public void handle(HttpServletRequest request, HttpServletResponse response, - AccessDeniedException accessDeniedException) throws IOException { - response.setContentType("application/json;charset=UTF-8"); - response.setStatus(403); - try { - response.getWriter().write(new JSONObject() - .put("timestamp", new Timestamp(System.currentTimeMillis())) - .put("status", 403) - .put("error", "Forbidden") - .put("message", "Access Denied. Please contact the system administrator to request access.") - .put("path", request.getServletPath()) - .toString()); - } catch (JSONException e) { - response.getWriter().write(""); - e.printStackTrace(); - } - } -} diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CustomLoginUrlAuthenticationEntryPoint.java b/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CustomLoginUrlAuthenticationEntryPoint.java deleted file mode 100644 index 068ca591b..000000000 --- a/src/main/java/eu/hbp/mip/configuration/SecurityUtils/CustomLoginUrlAuthenticationEntryPoint.java +++ /dev/null @@ -1,20 +0,0 @@ -package eu.hbp.mip.configuration.SecurityUtils; - -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -public class CustomLoginUrlAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint { - - public CustomLoginUrlAuthenticationEntryPoint(String url) { - super(url); - } - - @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED); - } -} diff --git a/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java b/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java index 72bcad789..50fbe43ae 100644 --- a/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java @@ -18,11 +18,6 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2; @EnableSwagger2 public class WebConfiguration { -// @Bean -// public String[] swaggerUiConfig() { -// return UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS; -// } - @Bean public Docket swaggerDocumentation() { return new Docket(DocumentationType.SWAGGER_2) diff --git a/src/main/java/eu/hbp/mip/controllers/UsersApi.java b/src/main/java/eu/hbp/mip/controllers/UsersApi.java index 1c60aba1e..c8b1193f8 100644 --- a/src/main/java/eu/hbp/mip/controllers/UsersApi.java +++ b/src/main/java/eu/hbp/mip/controllers/UsersApi.java @@ -36,7 +36,7 @@ public class UsersApi { UserDAO activeUser = activeUserService.getActiveUser(); - // Add the active user to a cookie + // Add the active user object to a cookie try { // TODO needed? Ask Manuel ObjectMapper mapper = new ObjectMapper(); @@ -53,7 +53,6 @@ public class UsersApi { return ResponseEntity.ok(activeUserService.getActiveUser()); } - // TODO Kostas, why not working? @ApiOperation(value = "The active user agrees to the NDA", response = UserDAO.class) @RequestMapping(value = "/agreeNDA", method = RequestMethod.POST) public ResponseEntity<UserDAO> activeUserServiceAgreesToNDA(@RequestBody(required = false) UserDAO userDAO) { diff --git a/src/main/java/eu/hbp/mip/services/ActiveUserService.java b/src/main/java/eu/hbp/mip/services/ActiveUserService.java index d925ce3f5..3d8815373 100644 --- a/src/main/java/eu/hbp/mip/services/ActiveUserService.java +++ b/src/main/java/eu/hbp/mip/services/ActiveUserService.java @@ -18,14 +18,14 @@ import javax.inject.Named; @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) public class ActiveUserService { - @Autowired - private UserRepository userRepository; - @Value("#{'${authentication.enabled}'}") private boolean authentication; private UserDAO user; + @Autowired + private UserRepository userRepository; + /** * Fetches the details of the active user. * If the user doesn't exist, it's created on the fly from the auth token. @@ -46,7 +46,7 @@ public class ActiveUserService { return user; } - // If authentication is ON + // If authentication is ON get user info from Token KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); IDToken idToken = keycloakPrincipal.getKeycloakSecurityContext().getIdToken(); diff --git a/src/main/java/eu/hbp/mip/utils/ClaimUtils.java b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java index 5872819bc..cdbe41c9e 100644 --- a/src/main/java/eu/hbp/mip/utils/ClaimUtils.java +++ b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java @@ -15,11 +15,11 @@ public class ClaimUtils { private static final Gson gson = new Gson(); public static String allDatasetsAllowedClaim() { - return "dataset_all"; + return "role_dataset_all"; } public static String getDatasetClaim(String datasetCode) { - return "dataset_" + datasetCode.toLowerCase(); + return "role_dataset_" + datasetCode.toLowerCase(); } public static boolean userHasDatasetsAuthorization(String username, Collection<? extends GrantedAuthority> authorities, diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 7ff94d087..30c82a447 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,6 @@ -# Configuration template for the portal running inside a Docker container +# Configuration for development purposes -# See http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +### DATABASE CONFIGURATION ### spring: portal-datasource: url: "jdbc:postgresql://127.0.0.1:5433/portal" @@ -8,7 +8,6 @@ spring: username: "portal" password: "portalpwd" driver-class-name: org.postgresql.Driver - data: jpa: repositories: @@ -18,12 +17,7 @@ spring: dialect: org.hibernate.dialect.PostgreSQL9Dialect ddl-auto: validate -# WEB FRONTEND -frontend: - loginUrl: "http://127.0.0.1/services/login/hbp" - redirectAfterLoginUrl: "http://127.0.0.1/" - redirectAfterLogoutUrl: "http://127.0.0.1/services/login/hbp" - +### LOG LEVELS ### logging: level: root: "ERROR" @@ -31,14 +25,14 @@ logging: eu: hbp: "DEBUG" -# EMBEDDED SERVER CONFIGURATION +### EMBEDDED SERVER CONFIGURATION ### server: servlet: contextPath: "/services" port: 8080 forward-headers-strategy: native -# ENDPOINTS +### ENDPOINTS ### endpoints: enabled: true health: @@ -46,12 +40,11 @@ endpoints: endpoint: "/health" sensitive: false -# External Services +### EXTERNAL SERVICES ### services: exareme: queryExaremeUrl: "http://127.0.0.1:9090/mining/query" algorithmsUrl: "http://127.0.0.1:9090/mining/algorithms.json" - galaxy: galaxyUrl: "http://127.0.0.1:8090" galaxyContext: "nativeGalaxy/workflows/list" @@ -59,11 +52,11 @@ services: galaxyUsername: "admin" galaxyPassword: "password" -# Authentication +### Authentication ### authentication: enabled: true -# Keycloak +### Keycloak ### keycloak: enabled: true auth-server-url: "http://127.0.0.1/auth" @@ -74,8 +67,7 @@ keycloak: secret: "dae83a6b-c769-4186-8383-f0984c6edf05" principal-attribute: "preferred_username" - # logoutUrl: {{ .Env.LOGOUT_URL }} - +### EXTERNAL FILES ### # Files are loaded from the resources files: pathologies_json: "classPath:/pathologies.json" -- GitLab From 12cbb8c2ca3b5af4af62665d8487b58de95912e7 Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Tue, 17 Nov 2020 10:37:56 -0800 Subject: [PATCH 27/52] Cleanup and beautification. --- src/main/java/eu/hbp/mip/MIPApplication.java | 5 - .../APIMetadataConfiguration.java} | 14 +- .../GalaxyAuthentication.java | 6 +- .../PersistenceConfiguration.java | 4 +- .../SecurityConfiguration.java | 6 +- .../eu/hbp/mip/controllers/AlgorithmsApi.java | 5 +- .../eu/hbp/mip/controllers/ExperimentApi.java | 2 +- .../hbp/mip/controllers/PathologiesApi.java | 2 +- .../java/eu/hbp/mip/controllers/UsersApi.java | 4 +- .../retrofit/RetroFitGalaxyClients.java | 6 +- .../eu/hbp/mip/model/DAOs/ArticleDAO.java | 151 ------------------ .../{model => models}/DAOs/ExperimentDAO.java | 6 +- .../mip/{model => models}/DAOs/UserDAO.java | 2 +- .../{model => models}/DTOs/AlgorithmDTO.java | 2 +- .../{model => models}/DTOs/ExperimentDTO.java | 4 +- .../{model => models}/DTOs/PathologyDTO.java | 2 +- .../galaxy/ErrorResponse.java | 2 +- .../galaxy/GalaxyWorkflowResult.java | 2 +- .../PostWorkflowToGalaxyDtoResponse.java | 2 +- .../{model => models}/galaxy/WorkflowDTO.java | 4 +- .../mip/repositories/ArticleRepository.java | 17 -- .../repositories/ExperimentRepository.java | 2 +- .../hbp/mip/repositories/UserRepository.java | 2 +- .../hbp/mip/services/ActiveUserService.java | 2 +- .../hbp/mip/services/ExperimentService.java | 12 +- .../services/ExperimentSpecifications.java | 2 +- .../java/eu/hbp/mip/utils/ClaimUtils.java | 2 +- 27 files changed, 41 insertions(+), 229 deletions(-) rename src/main/java/eu/hbp/mip/{configuration/WebConfiguration.java => configurations/APIMetadataConfiguration.java} (76%) rename src/main/java/eu/hbp/mip/{configuration => configurations}/GalaxyAuthentication.java (89%) rename src/main/java/eu/hbp/mip/{configuration => configurations}/PersistenceConfiguration.java (94%) rename src/main/java/eu/hbp/mip/{configuration => configurations}/SecurityConfiguration.java (96%) delete mode 100644 src/main/java/eu/hbp/mip/model/DAOs/ArticleDAO.java rename src/main/java/eu/hbp/mip/{model => models}/DAOs/ExperimentDAO.java (97%) rename src/main/java/eu/hbp/mip/{model => models}/DAOs/UserDAO.java (97%) rename src/main/java/eu/hbp/mip/{model => models}/DTOs/AlgorithmDTO.java (95%) rename src/main/java/eu/hbp/mip/{model => models}/DTOs/ExperimentDTO.java (97%) rename src/main/java/eu/hbp/mip/{model => models}/DTOs/PathologyDTO.java (93%) rename src/main/java/eu/hbp/mip/{model => models}/galaxy/ErrorResponse.java (96%) rename src/main/java/eu/hbp/mip/{model => models}/galaxy/GalaxyWorkflowResult.java (99%) rename src/main/java/eu/hbp/mip/{model => models}/galaxy/PostWorkflowToGalaxyDtoResponse.java (98%) rename src/main/java/eu/hbp/mip/{model => models}/galaxy/WorkflowDTO.java (95%) delete mode 100644 src/main/java/eu/hbp/mip/repositories/ArticleRepository.java diff --git a/src/main/java/eu/hbp/mip/MIPApplication.java b/src/main/java/eu/hbp/mip/MIPApplication.java index 9af69bdd9..f4001f93c 100644 --- a/src/main/java/eu/hbp/mip/MIPApplication.java +++ b/src/main/java/eu/hbp/mip/MIPApplication.java @@ -7,17 +7,12 @@ package eu.hbp.mip; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; @SpringBootApplication public class MIPApplication { - private static final Logger LOGGER = LoggerFactory.getLogger(MIPApplication.class); - public static void main(String[] args) { SpringApplication.run(MIPApplication.class, args); } - } diff --git a/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java b/src/main/java/eu/hbp/mip/configurations/APIMetadataConfiguration.java similarity index 76% rename from src/main/java/eu/hbp/mip/configuration/WebConfiguration.java rename to src/main/java/eu/hbp/mip/configurations/APIMetadataConfiguration.java index 50fbe43ae..7b9eab456 100644 --- a/src/main/java/eu/hbp/mip/configuration/WebConfiguration.java +++ b/src/main/java/eu/hbp/mip/configurations/APIMetadataConfiguration.java @@ -1,4 +1,4 @@ -package eu.hbp.mip.configuration; +package eu.hbp.mip.configurations; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -10,13 +10,9 @@ import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; -/** - * Created by mirco on 11.07.16. - */ - @Configuration @EnableSwagger2 -public class WebConfiguration { +public class APIMetadataConfiguration { @Bean public Docket swaggerDocumentation() { @@ -31,10 +27,8 @@ public class WebConfiguration { private ApiInfo metadata() { return new ApiInfoBuilder() .title("Medical Informatics Platform API") - .description("Serve the MIP Frontend") - .version("1.0") - .contact(new Contact("Mirco Nasuti", "https://www.unil.ch/lren/en/home.html", "mirco.nasuti@chuv.ch")) + .description("Serving the MIP Frontend") + .contact(new Contact("Kostas Filippopolitis", "https://github.com/KFilippopolitis", "kostasfilippop@gmail.com")) .build(); } - } diff --git a/src/main/java/eu/hbp/mip/configuration/GalaxyAuthentication.java b/src/main/java/eu/hbp/mip/configurations/GalaxyAuthentication.java similarity index 89% rename from src/main/java/eu/hbp/mip/configuration/GalaxyAuthentication.java rename to src/main/java/eu/hbp/mip/configurations/GalaxyAuthentication.java index 7910e809f..41888d3ea 100644 --- a/src/main/java/eu/hbp/mip/configuration/GalaxyAuthentication.java +++ b/src/main/java/eu/hbp/mip/configurations/GalaxyAuthentication.java @@ -1,13 +1,11 @@ -package eu.hbp.mip.configuration; +package eu.hbp.mip.configurations; import com.google.gson.Gson; import com.google.gson.JsonObject; -import eu.hbp.mip.configuration.SecurityConfiguration; import eu.hbp.mip.services.ActiveUserService; import eu.hbp.mip.utils.Logging; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; @@ -16,8 +14,6 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; import java.util.Base64; @RestController diff --git a/src/main/java/eu/hbp/mip/configuration/PersistenceConfiguration.java b/src/main/java/eu/hbp/mip/configurations/PersistenceConfiguration.java similarity index 94% rename from src/main/java/eu/hbp/mip/configuration/PersistenceConfiguration.java rename to src/main/java/eu/hbp/mip/configurations/PersistenceConfiguration.java index 909d2c1c8..181bc639c 100644 --- a/src/main/java/eu/hbp/mip/configuration/PersistenceConfiguration.java +++ b/src/main/java/eu/hbp/mip/configurations/PersistenceConfiguration.java @@ -1,4 +1,4 @@ -package eu.hbp.mip.configuration; +package eu.hbp.mip.configurations; import org.flywaydb.core.Flyway; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -36,7 +36,7 @@ public class PersistenceConfiguration { emfb.setDataSource(portalDataSource()); JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); emfb.setJpaVendorAdapter(vendorAdapter); - emfb.setPackagesToScan("eu.hbp.mip.model.DAOs"); + emfb.setPackagesToScan("eu.hbp.mip.models.DAOs"); return emfb; } diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java similarity index 96% rename from src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java rename to src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java index 796f900a5..c4dc2a28b 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java @@ -1,4 +1,4 @@ -package eu.hbp.mip.configuration; +package eu.hbp.mip.configurations; import org.keycloak.adapters.KeycloakConfigResolver; import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; @@ -21,8 +21,6 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; -// Keycloak security with keycloak-spring-boot-starter - @Controller @KeycloakConfiguration public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter { @@ -59,7 +57,7 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter @GetMapping(value = "/logout") public String logout() throws ServletException { request.logout(); - return "redirect:" + logoutRedirectURL; + return String.format("redirect:%s", logoutRedirectURL); } @Bean diff --git a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java index d42720344..454007298 100644 --- a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java +++ b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java @@ -8,16 +8,15 @@ import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import eu.hbp.mip.controllers.galaxy.retrofit.RetroFitGalaxyClients; import eu.hbp.mip.controllers.galaxy.retrofit.RetrofitClientInstance; -import eu.hbp.mip.model.DTOs.AlgorithmDTO; +import eu.hbp.mip.models.DTOs.AlgorithmDTO; import eu.hbp.mip.services.ActiveUserService; -import eu.hbp.mip.model.galaxy.WorkflowDTO; +import eu.hbp.mip.models.galaxy.WorkflowDTO; import eu.hbp.mip.utils.CustomResourceLoader; import eu.hbp.mip.utils.HTTPUtil; import eu.hbp.mip.utils.Logging; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; import org.springframework.http.ResponseEntity; diff --git a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java index d5fc161b1..79168b42a 100644 --- a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java +++ b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java @@ -1,6 +1,6 @@ package eu.hbp.mip.controllers; -import eu.hbp.mip.model.DTOs.ExperimentDTO; +import eu.hbp.mip.models.DTOs.ExperimentDTO; import eu.hbp.mip.services.ActiveUserService; import eu.hbp.mip.services.ExperimentService; import eu.hbp.mip.utils.JsonConverters; diff --git a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java index 0f3f3c2bb..2c9e08a7e 100644 --- a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java +++ b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java @@ -2,7 +2,7 @@ package eu.hbp.mip.controllers; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; -import eu.hbp.mip.model.DTOs.PathologyDTO; +import eu.hbp.mip.models.DTOs.PathologyDTO; import eu.hbp.mip.services.ActiveUserService; import eu.hbp.mip.utils.ClaimUtils; import eu.hbp.mip.utils.CustomResourceLoader; diff --git a/src/main/java/eu/hbp/mip/controllers/UsersApi.java b/src/main/java/eu/hbp/mip/controllers/UsersApi.java index c8b1193f8..0c07b5f05 100644 --- a/src/main/java/eu/hbp/mip/controllers/UsersApi.java +++ b/src/main/java/eu/hbp/mip/controllers/UsersApi.java @@ -2,13 +2,11 @@ package eu.hbp.mip.controllers; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import eu.hbp.mip.model.DAOs.UserDAO; -import eu.hbp.mip.repositories.UserRepository; +import eu.hbp.mip.models.DAOs.UserDAO; import eu.hbp.mip.services.ActiveUserService; import eu.hbp.mip.utils.Logging; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/eu/hbp/mip/controllers/galaxy/retrofit/RetroFitGalaxyClients.java b/src/main/java/eu/hbp/mip/controllers/galaxy/retrofit/RetroFitGalaxyClients.java index 612dce6ca..609163e89 100644 --- a/src/main/java/eu/hbp/mip/controllers/galaxy/retrofit/RetroFitGalaxyClients.java +++ b/src/main/java/eu/hbp/mip/controllers/galaxy/retrofit/RetroFitGalaxyClients.java @@ -1,9 +1,9 @@ package eu.hbp.mip.controllers.galaxy.retrofit; import com.google.gson.JsonObject; -import eu.hbp.mip.model.galaxy.GalaxyWorkflowResult; -import eu.hbp.mip.model.galaxy.PostWorkflowToGalaxyDtoResponse; -import eu.hbp.mip.model.galaxy.WorkflowDTO; +import eu.hbp.mip.models.galaxy.GalaxyWorkflowResult; +import eu.hbp.mip.models.galaxy.PostWorkflowToGalaxyDtoResponse; +import eu.hbp.mip.models.galaxy.WorkflowDTO; import retrofit2.Call; import retrofit2.http.*; diff --git a/src/main/java/eu/hbp/mip/model/DAOs/ArticleDAO.java b/src/main/java/eu/hbp/mip/model/DAOs/ArticleDAO.java deleted file mode 100644 index 541c50765..000000000 --- a/src/main/java/eu/hbp/mip/model/DAOs/ArticleDAO.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Created by mirco on 04.12.15. - */ - -package eu.hbp.mip.model.DAOs; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.annotations.ApiModel; -import org.springframework.validation.annotation.Validated; - -import javax.persistence.*; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; -import java.util.Date; - -@Entity -@Table(name = "`article`") -@ApiModel -@JsonInclude(JsonInclude.Include.NON_NULL) -@Validated -public class ArticleDAO { - - @Id - private String slug = null; - - @NotNull - @Size(min = 1, max = 255) - private String title; - - private String status = null; - - @Column(columnDefinition = "text", name = "abstract") - private String abstractText = null; - - @Column(columnDefinition = "text") - private String content = null; - - private Date publishedAt = null; - - private Date createdAt = null; - - private Date updatedAt = null; - - @ManyToOne - @JoinColumn(name = "createdby_username") - private UserDAO createdBy = null; - - @ManyToOne - @JoinColumn(name = "updatedby_username") - private UserDAO updatedBy = null; - - public ArticleDAO() { - /* - * Empty constructor is needed by Hibernate - */ - title = ""; - } - - - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - - public String getSlug() { - return slug; - } - - public void setSlug(String slug) { - this.slug = slug; - } - - - @JsonProperty("abstract") - public String getAbstract() { - return abstractText; - } - - public void setAbstract(String abstractText) { - this.abstractText = abstractText; - } - - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - - public Date getPublishedAt() { - return publishedAt; - } - - public void setPublishedAt(Date publishedAt) { - this.publishedAt = publishedAt; - } - - - public Date getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(Date createdAt) { - this.createdAt = createdAt; - } - - - public Date getUpdatedAt() { - return updatedAt; - } - - public void setUpdatedAt(Date updatedAt) { - this.updatedAt = updatedAt; - } - - - public UserDAO getCreatedBy() { - return createdBy; - } - - public void setCreatedBy(UserDAO createdBy) { - this.createdBy = createdBy; - } - - - public UserDAO getUpdatedBy() { - return updatedBy; - } - - public void setUpdatedBy(UserDAO updatedBy) { - this.updatedBy = updatedBy; - } - -} diff --git a/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java b/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java similarity index 97% rename from src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java rename to src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java index 2ee8e0ba4..e96f74c44 100644 --- a/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java +++ b/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java @@ -1,10 +1,10 @@ -package eu.hbp.mip.model.DAOs; +package eu.hbp.mip.models.DAOs; import com.fasterxml.jackson.annotation.JsonInclude; import com.google.gson.Gson; import com.google.gson.annotations.Expose; -import eu.hbp.mip.model.DTOs.AlgorithmDTO; -import eu.hbp.mip.model.DTOs.ExperimentDTO; +import eu.hbp.mip.models.DTOs.AlgorithmDTO; +import eu.hbp.mip.models.DTOs.ExperimentDTO; import eu.hbp.mip.utils.JsonConverters; import io.swagger.annotations.ApiModel; diff --git a/src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java b/src/main/java/eu/hbp/mip/models/DAOs/UserDAO.java similarity index 97% rename from src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java rename to src/main/java/eu/hbp/mip/models/DAOs/UserDAO.java index 0e1e9a9ff..a94e5da1c 100644 --- a/src/main/java/eu/hbp/mip/model/DAOs/UserDAO.java +++ b/src/main/java/eu/hbp/mip/models/DAOs/UserDAO.java @@ -2,7 +2,7 @@ * Created by mirco on 04.12.15. */ -package eu.hbp.mip.model.DAOs; +package eu.hbp.mip.models.DAOs; import com.fasterxml.jackson.annotation.JsonInclude; import com.google.gson.annotations.Expose; diff --git a/src/main/java/eu/hbp/mip/model/DTOs/AlgorithmDTO.java b/src/main/java/eu/hbp/mip/models/DTOs/AlgorithmDTO.java similarity index 95% rename from src/main/java/eu/hbp/mip/model/DTOs/AlgorithmDTO.java rename to src/main/java/eu/hbp/mip/models/DTOs/AlgorithmDTO.java index 0a49c5ad6..1a6d8b99d 100644 --- a/src/main/java/eu/hbp/mip/model/DTOs/AlgorithmDTO.java +++ b/src/main/java/eu/hbp/mip/models/DTOs/AlgorithmDTO.java @@ -1,4 +1,4 @@ -package eu.hbp.mip.model.DTOs; +package eu.hbp.mip.models.DTOs; import com.google.gson.annotations.SerializedName; diff --git a/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java b/src/main/java/eu/hbp/mip/models/DTOs/ExperimentDTO.java similarity index 97% rename from src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java rename to src/main/java/eu/hbp/mip/models/DTOs/ExperimentDTO.java index 64ade455b..797fe0925 100644 --- a/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java +++ b/src/main/java/eu/hbp/mip/models/DTOs/ExperimentDTO.java @@ -1,6 +1,6 @@ -package eu.hbp.mip.model.DTOs; +package eu.hbp.mip.models.DTOs; -import eu.hbp.mip.model.DAOs.ExperimentDAO; +import eu.hbp.mip.models.DAOs.ExperimentDAO; import java.util.Date; import java.util.List; diff --git a/src/main/java/eu/hbp/mip/model/DTOs/PathologyDTO.java b/src/main/java/eu/hbp/mip/models/DTOs/PathologyDTO.java similarity index 93% rename from src/main/java/eu/hbp/mip/model/DTOs/PathologyDTO.java rename to src/main/java/eu/hbp/mip/models/DTOs/PathologyDTO.java index 6a4580e81..2a8887b5f 100644 --- a/src/main/java/eu/hbp/mip/model/DTOs/PathologyDTO.java +++ b/src/main/java/eu/hbp/mip/models/DTOs/PathologyDTO.java @@ -1,4 +1,4 @@ -package eu.hbp.mip.model.DTOs; +package eu.hbp.mip.models.DTOs; import com.google.gson.annotations.SerializedName; diff --git a/src/main/java/eu/hbp/mip/model/galaxy/ErrorResponse.java b/src/main/java/eu/hbp/mip/models/galaxy/ErrorResponse.java similarity index 96% rename from src/main/java/eu/hbp/mip/model/galaxy/ErrorResponse.java rename to src/main/java/eu/hbp/mip/models/galaxy/ErrorResponse.java index 77a171358..2703e7425 100644 --- a/src/main/java/eu/hbp/mip/model/galaxy/ErrorResponse.java +++ b/src/main/java/eu/hbp/mip/models/galaxy/ErrorResponse.java @@ -3,7 +3,7 @@ * Copyright (c) 2019. MIT License */ -package eu.hbp.mip.model.galaxy; +package eu.hbp.mip.models.galaxy; import com.google.gson.annotations.SerializedName; diff --git a/src/main/java/eu/hbp/mip/model/galaxy/GalaxyWorkflowResult.java b/src/main/java/eu/hbp/mip/models/galaxy/GalaxyWorkflowResult.java similarity index 99% rename from src/main/java/eu/hbp/mip/model/galaxy/GalaxyWorkflowResult.java rename to src/main/java/eu/hbp/mip/models/galaxy/GalaxyWorkflowResult.java index 089a6a626..ca7d98fe5 100644 --- a/src/main/java/eu/hbp/mip/model/galaxy/GalaxyWorkflowResult.java +++ b/src/main/java/eu/hbp/mip/models/galaxy/GalaxyWorkflowResult.java @@ -3,7 +3,7 @@ * Copyright (c) 2019. MIT License */ -package eu.hbp.mip.model.galaxy; +package eu.hbp.mip.models.galaxy; import com.google.gson.annotations.SerializedName; diff --git a/src/main/java/eu/hbp/mip/model/galaxy/PostWorkflowToGalaxyDtoResponse.java b/src/main/java/eu/hbp/mip/models/galaxy/PostWorkflowToGalaxyDtoResponse.java similarity index 98% rename from src/main/java/eu/hbp/mip/model/galaxy/PostWorkflowToGalaxyDtoResponse.java rename to src/main/java/eu/hbp/mip/models/galaxy/PostWorkflowToGalaxyDtoResponse.java index edefd6326..96d8aa8ef 100644 --- a/src/main/java/eu/hbp/mip/model/galaxy/PostWorkflowToGalaxyDtoResponse.java +++ b/src/main/java/eu/hbp/mip/models/galaxy/PostWorkflowToGalaxyDtoResponse.java @@ -3,7 +3,7 @@ * Copyright (c) 2019. MIT License */ -package eu.hbp.mip.model.galaxy; +package eu.hbp.mip.models.galaxy; import com.google.gson.annotations.SerializedName; diff --git a/src/main/java/eu/hbp/mip/model/galaxy/WorkflowDTO.java b/src/main/java/eu/hbp/mip/models/galaxy/WorkflowDTO.java similarity index 95% rename from src/main/java/eu/hbp/mip/model/galaxy/WorkflowDTO.java rename to src/main/java/eu/hbp/mip/models/galaxy/WorkflowDTO.java index 4b01b3819..496ee2dfe 100644 --- a/src/main/java/eu/hbp/mip/model/galaxy/WorkflowDTO.java +++ b/src/main/java/eu/hbp/mip/models/galaxy/WorkflowDTO.java @@ -1,8 +1,8 @@ -package eu.hbp.mip.model.galaxy; +package eu.hbp.mip.models.galaxy; import com.google.gson.Gson; import com.google.gson.annotations.SerializedName; -import eu.hbp.mip.model.DTOs.AlgorithmDTO; +import eu.hbp.mip.models.DTOs.AlgorithmDTO; import java.util.*; diff --git a/src/main/java/eu/hbp/mip/repositories/ArticleRepository.java b/src/main/java/eu/hbp/mip/repositories/ArticleRepository.java deleted file mode 100644 index 5f6589ab5..000000000 --- a/src/main/java/eu/hbp/mip/repositories/ArticleRepository.java +++ /dev/null @@ -1,17 +0,0 @@ -package eu.hbp.mip.repositories; - -import eu.hbp.mip.model.DAOs.ArticleDAO; -import eu.hbp.mip.model.DAOs.UserDAO; -import org.springframework.data.repository.CrudRepository; - -/** - * Created by mirco on 11.07.16. - */ - -public interface ArticleRepository extends CrudRepository<ArticleDAO, String> { - Long countByTitle(String title); - - Iterable<ArticleDAO> findByCreatedBy(UserDAO user); - - Iterable<ArticleDAO> findByStatusOrCreatedBy(String status, UserDAO user); -} diff --git a/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java b/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java index 5f2c8dc76..4efb1613b 100644 --- a/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java +++ b/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java @@ -1,6 +1,6 @@ package eu.hbp.mip.repositories; -import eu.hbp.mip.model.DAOs.ExperimentDAO; +import eu.hbp.mip.models.DAOs.ExperimentDAO; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.CrudRepository; diff --git a/src/main/java/eu/hbp/mip/repositories/UserRepository.java b/src/main/java/eu/hbp/mip/repositories/UserRepository.java index 4a3a7c698..99ae0b1dc 100644 --- a/src/main/java/eu/hbp/mip/repositories/UserRepository.java +++ b/src/main/java/eu/hbp/mip/repositories/UserRepository.java @@ -1,6 +1,6 @@ package eu.hbp.mip.repositories; -import eu.hbp.mip.model.DAOs.UserDAO; +import eu.hbp.mip.models.DAOs.UserDAO; import org.springframework.data.repository.CrudRepository; /** diff --git a/src/main/java/eu/hbp/mip/services/ActiveUserService.java b/src/main/java/eu/hbp/mip/services/ActiveUserService.java index 3d8815373..e1a4a7350 100644 --- a/src/main/java/eu/hbp/mip/services/ActiveUserService.java +++ b/src/main/java/eu/hbp/mip/services/ActiveUserService.java @@ -1,6 +1,6 @@ package eu.hbp.mip.services; -import eu.hbp.mip.model.DAOs.UserDAO; +import eu.hbp.mip.models.DAOs.UserDAO; import eu.hbp.mip.repositories.UserRepository; import org.keycloak.KeycloakPrincipal; import org.keycloak.representations.IDToken; diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index 3f6bb87b0..829b4bfa0 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -12,12 +12,12 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import eu.hbp.mip.controllers.galaxy.retrofit.RetroFitGalaxyClients; import eu.hbp.mip.controllers.galaxy.retrofit.RetrofitClientInstance; -import eu.hbp.mip.model.DAOs.ExperimentDAO; -import eu.hbp.mip.model.DAOs.UserDAO; -import eu.hbp.mip.model.DTOs.AlgorithmDTO; -import eu.hbp.mip.model.DTOs.ExperimentDTO; -import eu.hbp.mip.model.galaxy.GalaxyWorkflowResult; -import eu.hbp.mip.model.galaxy.PostWorkflowToGalaxyDtoResponse; +import eu.hbp.mip.models.DAOs.ExperimentDAO; +import eu.hbp.mip.models.DAOs.UserDAO; +import eu.hbp.mip.models.DTOs.AlgorithmDTO; +import eu.hbp.mip.models.DTOs.ExperimentDTO; +import eu.hbp.mip.models.galaxy.GalaxyWorkflowResult; +import eu.hbp.mip.models.galaxy.PostWorkflowToGalaxyDtoResponse; import eu.hbp.mip.repositories.ExperimentRepository; import eu.hbp.mip.utils.ClaimUtils; import eu.hbp.mip.utils.Exceptions.*; diff --git a/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java b/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java index 076249e0b..8a79bf61e 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java @@ -1,6 +1,6 @@ package eu.hbp.mip.services; -import eu.hbp.mip.model.DAOs.ExperimentDAO; +import eu.hbp.mip.models.DAOs.ExperimentDAO; import org.springframework.data.jpa.domain.Specification; import javax.persistence.criteria.CriteriaBuilder; diff --git a/src/main/java/eu/hbp/mip/utils/ClaimUtils.java b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java index cdbe41c9e..117bead85 100644 --- a/src/main/java/eu/hbp/mip/utils/ClaimUtils.java +++ b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java @@ -1,7 +1,7 @@ package eu.hbp.mip.utils; import com.google.gson.Gson; -import eu.hbp.mip.model.DTOs.PathologyDTO; +import eu.hbp.mip.models.DTOs.PathologyDTO; import org.springframework.security.core.GrantedAuthority; import java.util.ArrayList; -- GitLab From f2a273e1d9ab50ed364066cf6e8bf8a420874af3 Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Tue, 17 Nov 2020 10:45:42 -0800 Subject: [PATCH 28/52] Small beautifications. --- .../{UsersApi.java => ActiveUserAPI.java} | 28 ++++--------------- ...{AlgorithmsApi.java => AlgorithmsAPI.java} | 2 +- ...{ExperimentApi.java => ExperimentAPI.java} | 2 +- ...athologiesApi.java => PathologiesAPI.java} | 2 +- 4 files changed, 8 insertions(+), 26 deletions(-) rename src/main/java/eu/hbp/mip/controllers/{UsersApi.java => ActiveUserAPI.java} (60%) rename src/main/java/eu/hbp/mip/controllers/{AlgorithmsApi.java => AlgorithmsAPI.java} (99%) rename src/main/java/eu/hbp/mip/controllers/{ExperimentApi.java => ExperimentAPI.java} (99%) rename src/main/java/eu/hbp/mip/controllers/{PathologiesApi.java => PathologiesAPI.java} (99%) diff --git a/src/main/java/eu/hbp/mip/controllers/UsersApi.java b/src/main/java/eu/hbp/mip/controllers/ActiveUserAPI.java similarity index 60% rename from src/main/java/eu/hbp/mip/controllers/UsersApi.java rename to src/main/java/eu/hbp/mip/controllers/ActiveUserAPI.java index 0c07b5f05..7e5209b75 100644 --- a/src/main/java/eu/hbp/mip/controllers/UsersApi.java +++ b/src/main/java/eu/hbp/mip/controllers/ActiveUserAPI.java @@ -1,7 +1,5 @@ package eu.hbp.mip.controllers; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import eu.hbp.mip.models.DAOs.UserDAO; import eu.hbp.mip.services.ActiveUserService; import eu.hbp.mip.utils.Logging; @@ -9,19 +7,19 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; -import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @RestController @RequestMapping(value = "/activeUser", produces = {APPLICATION_JSON_VALUE}) @Api(value = "/activeUser") -public class UsersApi { +public class ActiveUserAPI { @Autowired private ActiveUserService activeUserService; @@ -32,22 +30,6 @@ public class UsersApi { Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /activeUser", "Loading the details of the activeUser"); - UserDAO activeUser = activeUserService.getActiveUser(); - - // Add the active user object to a cookie - try { - // TODO needed? Ask Manuel - ObjectMapper mapper = new ObjectMapper(); - String userJSON = mapper.writeValueAsString(activeUser); - Cookie cookie = new Cookie("user", URLEncoder.encode(userJSON, "UTF-8")); - cookie.setSecure(true); - cookie.setPath("/"); - response.addCookie(cookie); - } catch (JsonProcessingException | UnsupportedEncodingException e) { - Logging.LogUserAction(activeUser.getUsername(), - "(GET) /activeUser", "Failed to add Cookie. Exception: " + e.getMessage()); - } - return ResponseEntity.ok(activeUserService.getActiveUser()); } diff --git a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java b/src/main/java/eu/hbp/mip/controllers/AlgorithmsAPI.java similarity index 99% rename from src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java rename to src/main/java/eu/hbp/mip/controllers/AlgorithmsAPI.java index 454007298..001409d25 100644 --- a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java +++ b/src/main/java/eu/hbp/mip/controllers/AlgorithmsAPI.java @@ -37,7 +37,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @RestController @RequestMapping(value = "/algorithms", produces = {APPLICATION_JSON_VALUE}) @Api(value = "/algorithms") -public class AlgorithmsApi { +public class AlgorithmsAPI { private static final Gson gson = new Gson(); diff --git a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java b/src/main/java/eu/hbp/mip/controllers/ExperimentAPI.java similarity index 99% rename from src/main/java/eu/hbp/mip/controllers/ExperimentApi.java rename to src/main/java/eu/hbp/mip/controllers/ExperimentAPI.java index 79168b42a..9c77111aa 100644 --- a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java +++ b/src/main/java/eu/hbp/mip/controllers/ExperimentAPI.java @@ -25,7 +25,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @RestController @RequestMapping(value = "/experiments", produces = {APPLICATION_JSON_VALUE}) @Api(value = "/experiments") -public class ExperimentApi { +public class ExperimentAPI { @Autowired private ActiveUserService activeUserService; diff --git a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java b/src/main/java/eu/hbp/mip/controllers/PathologiesAPI.java similarity index 99% rename from src/main/java/eu/hbp/mip/controllers/PathologiesApi.java rename to src/main/java/eu/hbp/mip/controllers/PathologiesAPI.java index 2c9e08a7e..f79052ff5 100644 --- a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java +++ b/src/main/java/eu/hbp/mip/controllers/PathologiesAPI.java @@ -27,7 +27,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @RestController @RequestMapping(value = "/pathologies", produces = {APPLICATION_JSON_VALUE}) @Api(value = "/pathologies") -public class PathologiesApi { +public class PathologiesAPI { private static final Gson gson = new Gson(); -- GitLab From 79ac778641580deecb2d948eaa1b61789816918d Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Wed, 18 Nov 2020 00:24:50 -0800 Subject: [PATCH 29/52] Removing webjars endpoint. --- .../eu/hbp/mip/configurations/SecurityConfiguration.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java index c4dc2a28b..dfc5d4e47 100644 --- a/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java @@ -25,12 +25,12 @@ import javax.servlet.http.HttpServletRequest; @KeycloakConfiguration public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter { - @Value("#{'${authentication.enabled}'}") - private boolean authenticationEnabled; - // Redirect to login page url private static final String logoutRedirectURL = "/sso/login"; + @Value("#{'${authentication.enabled}'}") + private boolean authenticationEnabled; + @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); @@ -38,7 +38,7 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter if (authenticationEnabled) { http.authorizeRequests() .antMatchers( - "/sso/login", "/webjars/**", + "/sso/login", "/v2/api-docs", "/swagger-ui/**", "/swagger-resources/**" // Swagger URLs ).permitAll() .antMatchers("/galaxy*", "/galaxy/*").hasRole("DATA MANAGER") -- GitLab From ab5c32f93ba94cf55a04e5eb74283c65f546cd17 Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Wed, 18 Nov 2020 00:45:35 -0800 Subject: [PATCH 30/52] Added filter order by on get /experiments and added property updated on experiment. --- pom.xml | 37 +- .../eu/hbp/mip/controllers/ExperimentApi.java | 27 +- .../eu/hbp/mip/model/DAOs/ExperimentDAO.java | 40 ++- .../eu/hbp/mip/model/DTOs/ExperimentDTO.java | 56 +-- .../repositories/ExperimentRepository.java | 2 +- .../hbp/mip/services/ExperimentService.java | 319 ++++++++++-------- .../services/ExperimentSpecifications.java | 45 ++- .../java/eu/hbp/mip/utils/ClaimUtils.java | 6 +- .../java/eu/hbp/mip/utils/JsonConverters.java | 4 +- .../migration/V7_0__NewDatabaseStructure.sql | 3 + 10 files changed, 335 insertions(+), 204 deletions(-) diff --git a/pom.xml b/pom.xml index 0c3f8a8c2..97d528b78 100644 --- a/pom.xml +++ b/pom.xml @@ -251,20 +251,31 @@ <artifactId>java-jwt</artifactId> <version>3.11.0</version> </dependency> - </dependencies> - <build> - <finalName>portal-backend</finalName> - <resources> - <resource> - <directory>src/main/resources</directory> - <includes> - <include>**/*.xml</include> - <include>**/*.json</include> - <include>**/*.csv</include> - <include>**/*.sql</include> - <include>**/*.conf</include> - <include>**/*.yml</include> <!-- Only for development --> + <dependency> + <groupId>com.google.code.svenson</groupId> + <artifactId>svenson</artifactId> + <version>1.5.8</version> + </dependency> + <!--<dependency> + <groupId>eu.hbp.mip</groupId> + <artifactId>portal-backend</artifactId> + <version>4.0.0</version> + </dependency>--> + </dependencies> + + <build> + <finalName>portal-backend</finalName> + <resources> + <resource> + <directory>src/main/resources</directory> + <includes> + <include>**/*.xml</include> + <include>**/*.json</include> + <include>**/*.csv</include> + <include>**/*.sql</include> + <include>**/*.conf</include> + <!-- <include>**/*.yml</include> Only for development --> </includes> <filtering>true</filtering> diff --git a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java index 322f33f4f..803c47572 100644 --- a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java +++ b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java @@ -1,13 +1,11 @@ package eu.hbp.mip.controllers; import eu.hbp.mip.model.DTOs.ExperimentDTO; -import eu.hbp.mip.model.UserInfo; import eu.hbp.mip.services.ExperimentService; import eu.hbp.mip.utils.JsonConverters; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; @@ -27,23 +25,34 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @Api(value = "/experiments") public class ExperimentApi { - @Autowired - private UserInfo userInfo; + private final ExperimentService experimentService; - @Autowired - private ExperimentService experimentService; + public ExperimentApi(ExperimentService experimentService) { + this.experimentService = experimentService; + } - @ApiOperation(value = "Get experiments", response = ExperimentDTO.class, responseContainer = "List") + @ApiOperation(value = "Get experiments", response = Map.class, responseContainer = "List") @RequestMapping(method = RequestMethod.GET) public ResponseEntity<String> getExperiments( @RequestParam(name = "name", required = false) String name, @RequestParam(name = "algorithm", required = false) String algorithm, @RequestParam(name = "shared", required = false) Boolean shared, @RequestParam(name = "viewed", required = false) Boolean viewed, + @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 ) { - Map experiments = experimentService.getExperiments(name, algorithm, shared, viewed, page, size, "(GET) /experiments"); + Map experiments = experimentService.getExperiments( + name, + algorithm, + shared, + viewed, + page, + size, + orderBy, + descending, + "(GET) /experiments"); return new ResponseEntity(experiments, HttpStatus.OK); } @@ -66,7 +75,7 @@ public class ExperimentApi { @ApiOperation(value = "Create a transient experiment", response = ExperimentDTO.class) - @RequestMapping(value = "/{transient}",method = RequestMethod.POST) + @RequestMapping(value = "/transient",method = RequestMethod.POST) public ResponseEntity<String> createTransientExperiment(Authentication authentication, @RequestBody ExperimentDTO experimentDTO) { experimentDTO = experimentService.createTransientExperiment(authentication, experimentDTO, "(POST) /experiments/transient"); diff --git a/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java b/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java index 2ee8e0ba4..f10f88742 100644 --- a/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java +++ b/src/main/java/eu/hbp/mip/model/DAOs/ExperimentDAO.java @@ -7,10 +7,10 @@ import eu.hbp.mip.model.DTOs.AlgorithmDTO; import eu.hbp.mip.model.DTOs.ExperimentDTO; import eu.hbp.mip.utils.JsonConverters; import io.swagger.annotations.ApiModel; +import org.svenson.JSONParser; import javax.persistence.*; -import java.util.Date; -import java.util.UUID; +import java.util.*; /** * Created by habfast on 21/04/16. @@ -65,6 +65,10 @@ public class ExperimentDAO { @Expose @Column(columnDefinition = "TIMESTAMP WITHOUT TIME ZONE") private Date created = new Date(); + + @Expose + @Column(columnDefinition = "TIMESTAMP WITHOUT TIME ZONE") + private Date updated; @Expose @Column(columnDefinition = "BOOLEAN") @@ -81,7 +85,7 @@ public class ExperimentDAO { success } - public enum MimeTypes { + public enum Type { ERROR("text/plain+error"), WARNING("text/plain+warning"), USER_WARNING("text/plain+user_error"), @@ -93,15 +97,15 @@ public class ExperimentDAO { HTML("text/html"), TEXT("text/plain"); - private String types; + private String type; //Constructor to initialize the instance variable - MimeTypes(String types) { - this.types = types; + Type(String type) { + this.type = type; } - public String getTypes() { - return this.types; + public String getType() { + return this.type; } } @@ -116,15 +120,25 @@ public class ExperimentDAO { ExperimentDTO experimentDTO = new ExperimentDTO(); experimentDTO.setAlgorithmDetails(JsonConverters.convertJsonStringToObject(this.algorithmDetails, AlgorithmDTO.class)); experimentDTO.setCreated(this.created); + experimentDTO.setUpdated(this.updated); + experimentDTO.setFinished(this.finished); experimentDTO.setCreatedBy(this.createdBy.getUsername()); experimentDTO.setName(this.name); - experimentDTO.setResult(JsonConverters.convertJsonStringToObject(this.result, ExperimentDTO.ResultDTO.class)); + experimentDTO.setResult(convertJsonStringToResult(this.result)); + experimentDTO.setStatus(this.status); experimentDTO.setShared(this.shared); experimentDTO.setUuid(this.uuid); experimentDTO.setViewed(this.viewed); 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; } @@ -180,6 +194,14 @@ public class ExperimentDAO { public void setCreated(Date created) { this.created = created; } + + public Date getUpdated() { + return updated; + } + + public void setUpdated(Date updated) { + this.updated = updated; + } public UUID getUuid() { return uuid; diff --git a/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java b/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java index 64ade455b..037ab5767 100644 --- a/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java +++ b/src/main/java/eu/hbp/mip/model/DTOs/ExperimentDTO.java @@ -3,7 +3,7 @@ package eu.hbp.mip.model.DTOs; import eu.hbp.mip.model.DAOs.ExperimentDAO; import java.util.Date; -import java.util.List; +import java.util.Map; import java.util.UUID; public class ExperimentDTO { @@ -12,9 +12,11 @@ public class ExperimentDTO { private String name; private String createdBy; private Date created; + private Date updated; + private Date finished; private Boolean shared; private Boolean viewed; - private ExperimentDTO.ResultDTO result; + private Map result; private ExperimentDAO.Status status; private String algorithm; @@ -71,6 +73,22 @@ public class ExperimentDTO { public void setCreated(Date created) { this.created = created; } + + public Date getUpdated() { + return updated; + } + + public void setUpdated(Date updated) { + this.updated = updated; + } + + public Date getFinished() { + return finished; + } + + public void setFinished(Date finished) { + this.finished = finished; + } public Boolean getShared() { return shared; @@ -88,11 +106,11 @@ public class ExperimentDTO { this.viewed = viewed; } - public ExperimentDTO.ResultDTO getResult() { + public Map getResult() { return result; } - public void setResult(ExperimentDTO.ResultDTO result) { + public void setResult(Map result) { this.result = result; } @@ -104,37 +122,25 @@ public class ExperimentDTO { this.status = status; } - public static class OutputDTO { + public static class ResultDTO { - private String data; - private ExperimentDAO.MimeTypes mimeTypes; + private Object data; + private ExperimentDAO.Type type; - public String getData() { + public Object getData() { return this.data; } - public void setData(String data) { + public void setData(Object data) { this.data = data; } - public ExperimentDAO.MimeTypes getMimeTypes() { - return mimeTypes; - } - - public void setMimeTypes(ExperimentDAO.MimeTypes mimeTypes) { - this.mimeTypes = mimeTypes; - } - } - - public static class ResultDTO { - private List<OutputDTO> result; - - public List<OutputDTO> getResult() { - return this.result; + public ExperimentDAO.Type getType() { + return type; } - public void setResult(List<OutputDTO> result) { - this.result = result; + public void setType(ExperimentDAO.Type type) { + this.type = type; } } } diff --git a/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java b/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java index 5f2c8dc76..7b30600e5 100644 --- a/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java +++ b/src/main/java/eu/hbp/mip/repositories/ExperimentRepository.java @@ -13,5 +13,5 @@ import java.util.UUID; public interface ExperimentRepository extends CrudRepository<ExperimentDAO, UUID>, JpaSpecificationExecutor<ExperimentDAO> { - Optional<ExperimentDAO> findByUuid(UUID experimentUuid); + ExperimentDAO findByUuid(UUID experimentUuid); } diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index 2590e1edd..12c0d1269 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -27,7 +27,7 @@ import eu.hbp.mip.utils.JsonConverters; import eu.hbp.mip.utils.Logging; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -46,8 +46,6 @@ import static java.lang.Thread.sleep; @Service public class ExperimentService { - @Autowired - private UserInfo userInfo; @Value("#{'${services.exareme.queryExaremeUrl}'}") private String queryExaremeUrl; @@ -62,11 +60,16 @@ public class ExperimentService { @Value("#{'${hbp.authentication.enabled:1}'}") private boolean authenticationIsEnabled; - @Autowired - private ExperimentRepository experimentRepository; - private static final Gson gson = new Gson(); + private final UserInfo userInfo; + private final ExperimentRepository experimentRepository; + + public ExperimentService(@Qualifier("userInfo") UserInfo userInfo, ExperimentRepository experimentRepository) { + this.userInfo = userInfo; + this.experimentRepository = experimentRepository; + } + /** * The getExperiments will retrieve the experiments from database according to the filters. * @@ -76,23 +79,26 @@ public class ExperimentService { * @param viewed is optional, in case it is required to filter the experiments by viewed * @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 + * @param descending is a boolean to determine if the experiments will be order by descending or ascending * @param endpoint is the endpoint that called the function * @return a list of mapped experiments */ - public Map getExperiments(String name,String algorithm, Boolean shared,Boolean viewed, int page, int size, String endpoint) { + public Map getExperiments(String name,String algorithm, Boolean shared,Boolean viewed, int page, int size, String orderBy, Boolean descending, String endpoint) { UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Listing my experiments."); if(size > 10 ) throw new BadRequestException("Invalid size input, max size is 10."); - - Specification<ExperimentDAO> spec = Specification.where(new ExperimentSpecifications.ExperimentWithName(name)) + 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.ExperimentWithViewed(viewed)) + .and(new ExperimentSpecifications.ExperimentOrderBy(orderBy, descending)); Pageable paging = PageRequest.of(page, size); - Page<ExperimentDAO> pageExperiments = experimentRepository.findAll(spec, paging); + Page<ExperimentDAO> pageExperiments = experimentRepository.findAll(spec, paging); List<ExperimentDAO> experimentDAOs = pageExperiments.getContent(); if (experimentDAOs.isEmpty()) @@ -124,7 +130,7 @@ public class ExperimentService { Logging.LogUserAction(user.getUsername(), endpoint, "Loading Experiment with uuid : " + uuid); - experimentDAO = loadExperiment(uuid).orElseThrow(() -> new ExperimentNotFoundException("Not found Experimnet with id = " + uuid)); + experimentDAO = loadExperiment(uuid, endpoint); if (!experimentDAO.isShared() && !experimentDAO.getCreatedBy().getUsername().equals(user.getUsername())) { Logging.LogUserAction(user.getUsername(), endpoint, "Accessing Experiment is unauthorized."); @@ -148,45 +154,16 @@ public class ExperimentService { UserDAO user = userInfo.getUser(); //Checking if check (POST) /experiments has proper input. - if (checkPostExperimentProperInput(experimentDTO)){ - Logging.LogUserAction(user.getUsername(), endpoint, - "Invalid input."); - throw new BadRequestException("Please provide proper input."); - } + checkPostExperimentProperInput(experimentDTO, endpoint); + // Get the type and name of algorithm String algorithmType = experimentDTO.getAlgorithmDetails().getType(); - String algorithmName = experimentDTO.getAlgorithmDetails().getName(); - StringBuilder parametersLogMessage = new StringBuilder(", Parameters:\n"); - experimentDTO.getAlgorithmDetails().getParameters().forEach( - params -> parametersLogMessage - .append(" ") - .append(params.getLabel()) - .append(" -> ") - .append(params.getValue()) - .append("\n") ); - Logging.LogUserAction(user.getUsername(), endpoint, "Executing " + algorithmName + parametersLogMessage); + algorithmParametersLogging(experimentDTO, endpoint); if (authenticationIsEnabled) { - // Getting the dataset from the experiment parameters - String experimentDatasets = null; - for (AlgorithmDTO.AlgorithmParamDTO parameter : experimentDTO.getAlgorithmDetails().getParameters()) { - if (parameter.getLabel().equals("dataset")) { - experimentDatasets = parameter.getValue(); - break; - } - } - - if (experimentDatasets == null || experimentDatasets.equals("")) { - Logging.LogUserAction(user.getUsername(), endpoint, - "A dataset should be specified to run an algorithm."); - throw new BadRequestException("Please provide at least one dataset to run the algorithm."); - } - - // --- Validating proper access rights on the datasets --- - if (!ClaimUtils.userHasDatasetsAuthorization(user.getUsername(), authentication.getAuthorities(), experimentDatasets)) { - throw new BadRequestException("You are not authorized to use these datasets."); - } + String experimentDatasets = getDatasetFromExperimentParameters(experimentDTO, endpoint); + ClaimUtils.validateAccessRightsOnDatasets(user.getUsername(), authentication.getAuthorities(), experimentDatasets); } // Run with the appropriate engine @@ -211,11 +188,7 @@ public class ExperimentService { UserDAO user = userInfo.getUser(); //Checking if check (POST) /experiments has proper input. - if (checkPostExperimentProperInput(experimentDTO)){ - Logging.LogUserAction(user.getUsername(), endpoint, - "Invalid input."); - throw new BadRequestException("Please provide proper input."); - } + checkPostExperimentProperInput(experimentDTO, endpoint); // Get the parameters List<AlgorithmDTO.AlgorithmParamDTO> algorithmParameters @@ -224,42 +197,17 @@ public class ExperimentService { // Get the type and name of algorithm String algorithmName = experimentDTO.getAlgorithmDetails().getName(); - if (!loadProperAlgorithms().contains(algorithmName)){ + if (!allowedTransientAlgorithms(algorithmName)){ Logging.LogUserAction(user.getUsername(), endpoint, "Not proper algorithm."); throw new BadRequestException("Please provide proper algorithm."); } - StringBuilder parametersLogMessage = new StringBuilder(", Parameters:\n"); - experimentDTO.getAlgorithmDetails().getParameters().forEach( - params -> parametersLogMessage - .append(" ") - .append(params.getLabel()) - .append(" -> ") - .append(params.getValue()) - .append("\n") ); - Logging.LogUserAction(user.getUsername(), endpoint, "Executing " + algorithmName + parametersLogMessage); + algorithmParametersLogging(experimentDTO, endpoint); if (authenticationIsEnabled) { - // Getting the dataset from the experiment parameters - String experimentDatasets = null; - for (AlgorithmDTO.AlgorithmParamDTO parameter : experimentDTO.getAlgorithmDetails().getParameters()) { - if (parameter.getLabel().equals("dataset")) { - experimentDatasets = parameter.getValue(); - break; - } - } - - if (experimentDatasets == null || experimentDatasets.equals("")) { - Logging.LogUserAction(user.getUsername(), endpoint, - "A dataset should be specified to run an algorithm."); - throw new BadRequestException("Please provide at least one dataset to run the algorithm."); - } - - // --- Validating proper access rights on the datasets --- - if (!ClaimUtils.userHasDatasetsAuthorization(user.getUsername(), authentication.getAuthorities(), experimentDatasets)) { - throw new BadRequestException("You are not authorized to use these datasets."); - } + String experimentDatasets = getDatasetFromExperimentParameters(experimentDTO, endpoint); + ClaimUtils.validateAccessRightsOnDatasets(user.getUsername(), authentication.getAuthorities(), experimentDatasets); } String body = gson.toJson(algorithmParameters); @@ -271,8 +219,11 @@ public class ExperimentService { // Results are stored in the experiment object ExaremeResult exaremeResult = runExaremeExperiment(url, body, experimentDTO); - experimentDTO.setResult(exaremeResult.result); - experimentDTO.setStatus((exaremeResult.code>= 400)? ExperimentDAO.Status.error: ExperimentDAO.Status.success); + + Logging.LogUserAction(user.getUsername(), endpoint, "Experiment with uuid: " + experimentDTO.getUuid() + "gave response code: " +exaremeResult.getCode() + " and result: "+ exaremeResult.getResults()); + + experimentDTO.setResult((exaremeResult.getCode()>= 400)? null: exaremeResult.getResults()); + experimentDTO.setStatus((exaremeResult.getCode()>= 400)? ExperimentDAO.Status.error: ExperimentDAO.Status.success); return experimentDTO; } @@ -289,41 +240,26 @@ public class ExperimentService { { ExperimentDAO experimentDAO; UserDAO user = userInfo.getUser(); - Logging.LogUserAction(user.getUsername(), endpoint, "Updating experiment with uuid : " + experimentDTO.getUuid() + "."); - //Checking if check (PUT) /experiments has proper input. - if (checkPutExperimentProperInput(experimentDTO)){ - Logging.LogUserAction(user.getUsername(), endpoint, - "Invalid input."); - throw new BadRequestException("Please provide proper input."); - } - - if((experimentDTO.getName() == null || experimentDTO.getName().length() == 0) - && experimentDTO.getShared() == null - && experimentDTO.getViewed() == null - && experimentDTO.getAlgorithmDetails() == null) - { - throw new BadRequestException("Input is required."); - } + Logging.LogUserAction(user.getUsername(), endpoint, "Updating experiment with uuid : " + uuid + "."); - experimentDAO = loadExperiment(uuid).orElseThrow(() -> new ExperimentNotFoundException("Not found Experimnet with id = " + uuid)); + experimentDAO = loadExperiment(uuid, endpoint); + //Verify (PATCH) /experiments non editable fields. + verifyPatchExperimentNonEditableFields(uuid, experimentDTO, experimentDAO, endpoint); + if (!experimentDAO.getCreatedBy().getUsername().equals(user.getUsername())) throw new UnauthorizedException("You don't have access to the experiment."); if(experimentDTO.getName() != null && experimentDTO.getName().length() != 0) - { experimentDAO.setName(experimentDTO.getName()); - } if(experimentDTO.getShared() != null) - { experimentDAO.setShared(experimentDTO.getShared()); - } if(experimentDTO.getViewed() != null) - { experimentDAO.setViewed(experimentDTO.getViewed()); - } + + experimentDAO.setUpdated(new Date()); try { experimentRepository.save(experimentDAO); @@ -332,7 +268,8 @@ public class ExperimentService { Logging.LogUserAction(user.getUsername(), endpoint, "Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); throw new InternalServerError(e.getMessage()); } - Logging.LogUserAction(user.getUsername(), endpoint, "Updated experiment with uuid : " + experimentDTO.getUuid() + "."); + + Logging.LogUserAction(user.getUsername(), endpoint, "Updated experiment with uuid : " + uuid + "."); experimentDTO = experimentDAO.convertToDTO(); return experimentDTO; @@ -350,42 +287,125 @@ public class ExperimentService { UserDAO user = userInfo.getUser(); Logging.LogUserAction(user.getUsername(), endpoint, "Deleting experiment with uuid : " + uuid + "."); - experimentDAO = loadExperiment(uuid).orElseThrow(() -> new ExperimentNotFoundException("Not found Experimnet with id = " + uuid)); + experimentDAO = loadExperiment(uuid, endpoint); if (!experimentDAO.getCreatedBy().getUsername().equals(user.getUsername())) throw new UnauthorizedException("You don't have access to the experiment."); - experimentRepository.delete(experimentDAO); + try { + experimentRepository.delete(experimentDAO); + } + catch (Exception e){ + Logging.LogUserAction(user.getUsername(), endpoint, "Attempted to delete an experiment to database but an error ocurred : " + e.getMessage() + "."); + throw new InternalServerError(e.getMessage()); + } Logging.LogUserAction(user.getUsername(), endpoint, "Deleted experiment with uuid : " + uuid + "."); } // /* ------------------------------- PRIVATE METHODS ----------------------------------------------------*/ - private boolean checkPostExperimentProperInput(ExperimentDTO experimentDTO) + + private void checkPostExperimentProperInput(ExperimentDTO experimentDTO, String endpoint) { - return experimentDTO.getShared() != null - || experimentDTO.getViewed() != null - || experimentDTO.getCreated() != null - || experimentDTO.getCreatedBy() != null - || experimentDTO.getResult() != null - || experimentDTO.getStatus() != null - || experimentDTO.getUuid() != null; + + boolean properInput = + experimentDTO.getShared() == null + && experimentDTO.getViewed() == null + && experimentDTO.getCreated() == null + && experimentDTO.getCreatedBy() == null + && experimentDTO.getResult() == null + && experimentDTO.getStatus() == null + && experimentDTO.getUuid() == null; + + if (!properInput){ + Logging.LogUserAction(userInfo.getUser().getUsername(), endpoint, "Invalid input."); + throw new BadRequestException("Please provide proper input."); + } } - private List<String> loadProperAlgorithms() + private boolean allowedTransientAlgorithms(String algorithmName) { List<String> properAlgorithms = new ArrayList<>(); - properAlgorithms.add("histograms"); - properAlgorithms.add("descriptive_stats"); - return properAlgorithms; + properAlgorithms.add("MULTIPLE_HISTOGRAMS"); + properAlgorithms.add("DESCRIPTIVE_STATS"); + return properAlgorithms.contains(algorithmName); } - private boolean checkPutExperimentProperInput(ExperimentDTO experimentDTO) + private void verifyPatchExperimentNonEditableFields(String uuid , ExperimentDTO experimentDTO, ExperimentDAO experimentDAO, String endpoint) { - return experimentDTO.getUuid() != null - || experimentDTO.getCreated() != null - || experimentDTO.getResult() != null - || experimentDTO.getStatus() != null; + if(experimentDTO.getUuid() != null && experimentDTO.getUuid().toString().compareTo(uuid) != 0){ + Logging.LogUserAction(userInfo.getUser().getUsername(), endpoint, "Uuid is not editable."); + throw new BadRequestException("Uuid is not editable."); + } + + if(experimentDTO.getAlgorithm() != null && experimentDTO.getAlgorithm().compareTo(experimentDAO.getAlgorithm()) != 0){ + Logging.LogUserAction(userInfo.getUser().getUsername(), endpoint, "Algorithm is not editable."); + throw new BadRequestException("Algorithm is not editable."); + } + + if(experimentDTO.getCreated() != null && experimentDTO.getCreatedBy().compareTo(experimentDAO.getCreatedBy().getUsername()) != 0){ + Logging.LogUserAction(userInfo.getUser().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){ + Logging.LogUserAction(userInfo.getUser().getUsername(), endpoint, "AlgorithmDetails is not editable."); + throw new BadRequestException("AlgorithmDetails is not editable."); + } + + if(experimentDTO.getCreated() != null && experimentDTO.getAlgorithm().compareTo(experimentDAO.getAlgorithm()) != 0){ + Logging.LogUserAction(userInfo.getUser().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){ + Logging.LogUserAction(userInfo.getUser().getUsername(), endpoint, "Status is not editable."); + throw new BadRequestException("Status is not editable."); + } + + if(experimentDTO.getStatus() != null && experimentDTO.getStatus().compareTo(experimentDAO.getStatus()) != 0){ + Logging.LogUserAction(userInfo.getUser().getUsername(), endpoint, "Status is not editable."); + throw new BadRequestException("Status is not editable."); + } + } + + private void algorithmParametersLogging(ExperimentDTO experimentDTO, String endpoint) { + UserDAO user = userInfo.getUser() ; + String algorithmName = experimentDTO.getAlgorithm(); + StringBuilder parametersLogMessage = new StringBuilder(", Parameters:\n"); + experimentDTO.getAlgorithmDetails().getParameters().forEach( + params -> parametersLogMessage + .append(" ") + .append(params.getLabel()) + .append(" -> ") + .append(params.getValue()) + .append("\n") ); + Logging.LogUserAction(user.getUsername(), endpoint, "Executing " + algorithmName + parametersLogMessage); + } + + /** + * The getDatasetFromExperimentParameters will retrieve the dataset from the experiment parameters + * + * @param experimentDTO is the experiment information + * @param endpoint is the endpoint that called the function + * @return the dataset from the experiment + */ + private String getDatasetFromExperimentParameters(ExperimentDTO experimentDTO, String endpoint) { + + String experimentDatasets = null; + for (AlgorithmDTO.AlgorithmParamDTO parameter : experimentDTO.getAlgorithmDetails().getParameters()) { + if (parameter.getLabel().equals("dataset")) { + experimentDatasets = parameter.getValue(); + break; + } + } + + if (experimentDatasets == null || experimentDatasets.equals("")) { + Logging.LogUserAction(userInfo.getUser().getUsername(), endpoint, + "A dataset should be specified to run an algorithm."); + throw new BadRequestException("Please provide at least one dataset to run the algorithm."); + } + return experimentDatasets; } /** @@ -394,14 +414,26 @@ public class ExperimentService { * @param uuid is the id of the experiment to be retrieved * @return the experiment information that was retrieved from database */ - private Optional<ExperimentDAO> loadExperiment(String uuid){ - - + private ExperimentDAO loadExperiment(String uuid, String endpoint){ UUID experimentUuid ; + ExperimentDAO experimentDAO; + + try { + experimentUuid = UUID.fromString(uuid); + } + catch (Exception e) + { + Logging.LogUserAction(userInfo.getUser().getUsername(), endpoint, e.getMessage()); + throw new BadRequestException(e.getMessage()); + } - experimentUuid = Optional.of(UUID.fromString(uuid)).orElseThrow(() -> new IllegalArgumentException("Invalid input uuid:"+ uuid)); + experimentDAO = experimentRepository.findByUuid(experimentUuid); + if (experimentDAO == null){ + Logging.LogUserAction(userInfo.getUser().getUsername(), endpoint, "Experiment with uuid : " + uuid + "was not found."); + throw new ExperimentNotFoundException("Experiment with uuid : " + uuid + " was not found."); + } - return experimentRepository.findByUuid(experimentUuid); + return experimentDAO; } /** @@ -411,6 +443,7 @@ public class ExperimentService { * @return the experiment information that was inserted into the database */ private ExperimentDAO createExperimentInTheDatabase(ExperimentDTO experimentDTO, String endpoint) { + UserDAO user = userInfo.getUser(); ExperimentDAO experimentDAO = new ExperimentDAO(); @@ -420,6 +453,7 @@ public class ExperimentService { experimentDAO.setAlgorithm(experimentDTO.getAlgorithm()); experimentDAO.setName(experimentDTO.getName()); experimentDAO.setStatus(ExperimentDAO.Status.pending); + try { experimentRepository.save(experimentDAO); } @@ -456,6 +490,7 @@ public class ExperimentService { private void finishExperiment(ExperimentDAO experimentDAO, String endpoint) { experimentDAO.setFinished(new Date()); + try { experimentRepository.save(experimentDAO); } @@ -508,8 +543,10 @@ public class ExperimentService { // Results are stored in the experiment object ExaremeResult exaremeResult = runExaremeExperiment(url, body, finalExperimentDTO); - experimentDAO.setResult(JsonConverters.convertObjectToJsonString(exaremeResult.result)); - experimentDAO.setStatus((exaremeResult.code>= 400)? ExperimentDAO.Status.error: ExperimentDAO.Status.success); + Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Experiment with uuid: " + experimentDAO.getUuid() + "gave response code: " +exaremeResult.getCode() + " and result: "+ exaremeResult.getResults()); + + experimentDAO.setResult((exaremeResult.getCode()>= 400)? null : JsonConverters.convertObjectToJsonString(exaremeResult.getResults())); + experimentDAO.setStatus((exaremeResult.getCode()>= 400)? ExperimentDAO.Status.error: ExperimentDAO.Status.success); } catch (Exception e) { Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "There was an exception: " + e.getMessage()); @@ -539,13 +576,13 @@ public class ExperimentService { code = HTTPUtil.sendPost(url, body, results); } catch (Exception e){ - throw new InternalServerError("Error occured : "+ e.getMessage()); + throw new InternalServerError("Error occurred : "+ e.getMessage()); } Logging.LogExperimentAction(experimentDTO.getName(), experimentDTO.getUuid(), "Algorithm finished with code: " + code); // Results are stored in the experiment object - ExperimentDTO.ResultDTO resultDTO = JsonConverters.convertJsonStringToObject(String.valueOf(results), ExperimentDTO.ResultDTO.class); - return new ExaremeResult(code, resultDTO); + Map resultsDTO = ExperimentDAO.convertJsonStringToResult(String.valueOf(results)); + return new ExaremeResult(code, resultsDTO); } @@ -952,21 +989,21 @@ public class ExperimentService { return returnError; } - final class ExaremeResult { - private final int code; - private final ExperimentDTO.ResultDTO result; + static class ExaremeResult { + private int code; + private Map results; - public ExaremeResult(int code, ExperimentDTO.ResultDTO result) { + public ExaremeResult(int code, Map results) { this.code = code; - this.result = result; + this.results = results; } public int getCode() { return code; } - public ExperimentDTO.ResultDTO getResult() { - return result; + public Map 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 076249e0b..f59faa9ed 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java @@ -1,12 +1,15 @@ package eu.hbp.mip.services; import eu.hbp.mip.model.DAOs.ExperimentDAO; +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 java.util.ArrayList; +import java.util.List; public class ExperimentSpecifications { public static class ExperimentWithName implements Specification<ExperimentDAO> { @@ -65,7 +68,7 @@ public class ExperimentSpecifications { } } - public static class ExperimentWithShared implements org.springframework.data.jpa.domain.Specification<ExperimentDAO> { + public static class ExperimentWithShared implements Specification<ExperimentDAO> { private Boolean shared; @@ -79,7 +82,47 @@ public class ExperimentSpecifications { } return cb.equal(root.get("shared"), this.shared); } + } + + public static class ExperimentOrderBy implements Specification<ExperimentDAO> { + + private String orderBy; + private Boolean descending ; + public ExperimentOrderBy(String orderBy, Boolean descending){ + if (properColumnToBeOrderedBy(orderBy)) + this.orderBy = orderBy; + else + throw new BadRequestException("Please provide proper column to order by."); + if(descending == null) + this.descending = true; + else + this.descending = descending; + } + public Predicate toPredicate(Root<ExperimentDAO> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) { + if (descending) { + criteriaQuery.orderBy(cb.desc(root.get(orderBy))); + } else { + criteriaQuery.orderBy(cb.asc(root.get(orderBy))); + } + return cb.isTrue(cb.literal(true)); + } + + } + + public static boolean properColumnToBeOrderedBy(String column){ + { + List<String> properColumns = new ArrayList<>(); + properColumns.add("uuid"); + properColumns.add("name"); + properColumns.add("created_by_username"); + properColumns.add("algorithm"); + properColumns.add("created"); + properColumns.add("status"); + properColumns.add("shared"); + properColumns.add("viewed"); + return properColumns.contains(column); + } } } diff --git a/src/main/java/eu/hbp/mip/utils/ClaimUtils.java b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java index 5872819bc..dea4cd0b5 100644 --- a/src/main/java/eu/hbp/mip/utils/ClaimUtils.java +++ b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java @@ -2,6 +2,7 @@ package eu.hbp.mip.utils; import com.google.gson.Gson; import eu.hbp.mip.model.DTOs.PathologyDTO; +import eu.hbp.mip.utils.Exceptions.BadRequestException; import org.springframework.security.core.GrantedAuthority; import java.util.ArrayList; @@ -22,7 +23,7 @@ public class ClaimUtils { return "dataset_" + datasetCode.toLowerCase(); } - public static boolean userHasDatasetsAuthorization(String username, Collection<? extends GrantedAuthority> authorities, + public static void validateAccessRightsOnDatasets(String username, Collection<? extends GrantedAuthority> authorities, String experimentDatasets) { List<String> userClaims = Arrays.asList(authorities.toString().toLowerCase() @@ -37,13 +38,12 @@ public class ClaimUtils { if (!userClaims.contains(datasetRole.toLowerCase())) { Logging.LogUserAction(username, "(POST) /experiments/runAlgorithm", "You are not allowed to use dataset: " + dataset); - return false; + throw new BadRequestException("You are not authorized to use these datasets."); } } Logging.LogUserAction(username, "(POST) /experiments/runAlgorithm", "User is authorized to use the datasets: " + experimentDatasets); } - return true; } public static String getAuthorizedPathologies(String username, Collection<? extends GrantedAuthority> authorities, diff --git a/src/main/java/eu/hbp/mip/utils/JsonConverters.java b/src/main/java/eu/hbp/mip/utils/JsonConverters.java index 52305124a..74d01b851 100644 --- a/src/main/java/eu/hbp/mip/utils/JsonConverters.java +++ b/src/main/java/eu/hbp/mip/utils/JsonConverters.java @@ -7,7 +7,7 @@ import com.google.gson.Gson; import java.lang.reflect.Type; public class JsonConverters { - Gson gson = new Gson(); + private static final Gson gson = new Gson(); public static String convertObjectToJsonString(Object object) { ObjectMapper mapper = new ObjectMapper(); @@ -22,6 +22,6 @@ public class JsonConverters { public static <T> T convertJsonStringToObject(String jsonString, Type typeOfT) { if(jsonString == null || jsonString.isEmpty()) return null; - return new Gson().fromJson(jsonString, typeOfT); + return gson.fromJson(jsonString, typeOfT); } } diff --git a/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql b/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql index 73a15addd..5e1bcc6fb 100644 --- a/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql +++ b/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql @@ -15,6 +15,9 @@ RENAME resultsviewed TO viewed; ALTER TABLE experiment RENAME workflowstatus TO status; +ALTER TABLE experiment +ADD COLUMN updated timestamp without time zone; + ALTER TABLE experiment ADD COLUMN algorithm text; -- GitLab From 511c4f3979acccbb6ddfa0f40604e73bd8589e30 Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Wed, 18 Nov 2020 01:55:25 -0800 Subject: [PATCH 31/52] User updated if info changed and csrf disabled on development. --- docker/README.md | 3 ++- .../mip/configurations/SecurityConfiguration.java | 7 ++++++- .../eu/hbp/mip/services/ActiveUserService.java | 14 +++++--------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/docker/README.md b/docker/README.md index da8299413..361c5ae56 100644 --- a/docker/README.md +++ b/docker/README.md @@ -74,4 +74,5 @@ To use this image, you need a running instance of PostgreSQL and to configure th * DATA_CENTER_LOCATION: Location of the datacenter, used when reporting errors to Bugsnag -# TODO Refactor variables \ No newline at end of file +# TODO Refactor variables +# TODO Refactor variables on mip-deployent as well \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java index dfc5d4e47..21a494b88 100644 --- a/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java @@ -25,7 +25,7 @@ import javax.servlet.http.HttpServletRequest; @KeycloakConfiguration public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter { - // Redirect to login page url + // Upon logout, redirect to login page url private static final String logoutRedirectURL = "/sso/login"; @Value("#{'${authentication.enabled}'}") @@ -36,6 +36,11 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter super.configure(http); if (authenticationEnabled) { + + // Used for development with authentication turned on. + // Should not be enabled on production. + http.csrf().disable(); + http.authorizeRequests() .antMatchers( "/sso/login", diff --git a/src/main/java/eu/hbp/mip/services/ActiveUserService.java b/src/main/java/eu/hbp/mip/services/ActiveUserService.java index 5004e1f7f..61bad973c 100644 --- a/src/main/java/eu/hbp/mip/services/ActiveUserService.java +++ b/src/main/java/eu/hbp/mip/services/ActiveUserService.java @@ -46,19 +46,15 @@ public class ActiveUserService { return user; } - - // TODO Update user if new values are providedTO // If authentication is ON get user info from Token KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); IDToken idToken = keycloakPrincipal.getKeycloakSecurityContext().getIdToken(); - UserDAO userInDatabase = userRepository.findByUsername(idToken.getPreferredUsername()); - if (userInDatabase != null) { - user = userInDatabase; - } else { - UserDAO newUser = new UserDAO(idToken.getPreferredUsername(), idToken.getName(), idToken.getEmail()); - userRepository.save(newUser); - user = newUser; + user = new UserDAO(idToken.getPreferredUsername(), idToken.getName(), idToken.getEmail()); + + UserDAO userInDatabase = userRepository.findByUsername(user.getUsername()); + if (userInDatabase == null || !userInDatabase.equals(user)) { + userRepository.save(user); } return user; } -- GitLab From 411d8f95b7e413395dd0916c5138605c8edd5284 Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Wed, 18 Nov 2020 04:37:38 -0800 Subject: [PATCH 32/52] Env variables cleanup. --- docker/README.md | 77 +++++-------------- docker/config/application.tmpl | 69 ++++++++++------- .../configurations/SecurityConfiguration.java | 8 +- .../eu/hbp/mip/controllers/ExperimentAPI.java | 19 +++-- src/main/resources/application.yml | 64 ++++++++------- 5 files changed, 113 insertions(+), 124 deletions(-) diff --git a/docker/README.md b/docker/README.md index 361c5ae56..eb3c76498 100644 --- a/docker/README.md +++ b/docker/README.md @@ -7,72 +7,37 @@ To use this image, you need a running instance of PostgreSQL and to configure the software using the following environment variables. -### DATABASES - -* PORTAL_DB_URL: JDBC URL to connect to the portal database, default value is "jdbc:postgresql://172.22.0.1:5432/portal". -* PORTAL_DB_SCHEMA: Database schema, default value is "public". -* PORTAL_DB_USER: User to use when connecting to the portal database, default value is "postgres". -* PORTAL_DB_PASSWORD: Password to use when connecting to the portal database. - -* META_DB_URL: JDBC URL to connect to the metadata database, default value is "jdbc:postgresql://172.22.0.1:5432/meta". -* META_DB_SCHEMA: Database schema, default value is "public". -* META_DB_USER: User to use when connecting to the metadata database. -* META_DB_PASSWORD: Password to use when connecting to the metadata database. - -* FEATURES_DB_URL: JDBC URL to connect to the science database, default value is "jdbc:postgresql://172.22.0.1:5433/features". -* FEATURES_DB_SCHEMA: Database schema, default value is "public". -* FEATURES_DB_USER: User to use when connecting to the science database, default value is "postgres". -* FEATURES_DB_PASSWORD: Password to use when connecting to the science database. -* FEATURES_DB_MAIN_TABLE: Table that contains the scientific data to use, default value is "features". - - -### OAUTH2 LOGIN - -* AUTHENTICATION: "0" to disable authentication or "1" to enable authentication, default value is "1". -* CLIENT_ID: required when authentication is turned on, client ID for the [OpenID server of HBP](https://services.humanbrainproject.eu/oidc/). -* CLIENT_SECRET: required when authentication is turned on, client secret for the [OpenID server of HBP](https://services.humanbrainproject.eu/oidc/). -* TOKEN_URI: default to "https://services.humanbrainproject.eu/oidc/token". -* AUTH_URI: default to "https://services.humanbrainproject.eu/oidc/authorize". -* USER_INFO_URI: default to "https://services.humanbrainproject.eu/oidc/userInfo". -* REVOKE_TOKEN_URI "https://services.humanbrainproject.eu/oidc/slo". - - -### WEB FRONTEND - -* FRONTEND_LOGIN_URL: URL to redirect to when login is required. Default to "http://frontend/services/login/hbp". -* FRONTEND_AFTER_LOGIN_URL: URL to redirect after login. Default to "http://frontend/home". -* FRONTEND_AFTER_LOGOUT_URL: URL to redirect to after logout. Default to "http://frontend/services/login/hbp". - - -### LOGGING - +### LOG LEVELS ### * LOG_LEVEL: log level for the developer added logs. Default is "ERROR". * LOG_LEVEL_FRAMEWORK: log level for all the framework logs. Default is "ERROR". -### ENDPOINTS +### AUTHENTICATION ### +* AUTHENTICATION: true for production, false for development. -* EXAREME_URL: URL to Exareme server, default value is "http://hbps2.chuv.ch:9090". -* WORKFLOW_URL: URL to Workflow server -* JWT_SECRET: "secret" -### EMBEDDED SERVER CONFIGURATION +### RELEASE STAGE ### +* PRODUCTION: Deployed on production? (True/False) Default is True. -* CONTEXT_PATH: context path appended to all services running in this container. Default to "/services". -* SESSION_TIMEOUT: Timeout in milliseconds for session expiration. Default to 2592000. -### PROXY +### DATABASE CONFIGURATION ### +* PORTAL_DB_URL: JDBC URL to connect to the portal database, default value is "jdbc:postgresql://127.0.0.1:5432/portal". +* PORTAL_DB_SCHEMA: Database schema, default value is "public". +* PORTAL_DB_USER: User to use when connecting to the portal database, default value is "postgres". +* PORTAL_DB_PASSWORD: Password to use when connecting to the portal database. -* HTTP_PROXY_HOST: HTTP proxy host -* HTTP_PROXY_PORT: HTTP proxy port -* HTTPS_PROXY_HOST: HTTPS proxy host -* HTTPS_PROXY_PORT: HTTPS proxy port -## ERROR REPORTING +### EXTERNAL SERVICES ### +* EXAREME_URL: URL to Exareme server. Default is "http://localhost:9090" . -* RELEASE_STAGE: Release stage used when reporting errors to Bugsnag. Values are dev, staging, production -* DATA_CENTER_LOCATION: Location of the datacenter, used when reporting errors to Bugsnag +* GALAXY_URL: URL to Workflow server. Default is "http://localhost:8090/" . +* GALAXY_API_KEY: The api key to authorize galaxy requests. +* GALAXY_USERNAME: The username of galaxy user to be able to embed the frame. +* GALAXY_PASSWORD: The password of galaxy user. -# TODO Refactor variables -# TODO Refactor variables on mip-deployent as well \ No newline at end of file +### KEYCLOAK ### +* KEYCLOAK_AUTH_URL: Keycloak authentication URL. +* KEYCLOAK_REALM: Keycloak realm user for authentication. +* KEYCLOAK_CLIENT_ID: The keycloak client id. +* KEYCLOAK_CLIENT_SECRET: The keycloak secret to be able to authenticate. \ No newline at end of file diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index 257bfa087..423baccff 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -1,5 +1,24 @@ # Configuration template for the portal running inside a Docker container +### LOG LEVELS ### +logging: + level: + root: {{ default .Env.LOG_LEVEL_FRAMEWORK "ERROR" }} + org: {{ default .Env.LOG_LEVEL_FRAMEWORK "ERROR" }} + eu: + hbp: {{ default .Env.LOG_LEVEL "INFO" }} + + +### AUTHENTICATION ### +authentication: + enabled: {{ default .Env.AUTHENTICATION "true" }} + + +### RELEASE STAGE ### +release_stage: + production: {{ default .Env.PRODUCTION "true" }} + + ### DATABASE CONFIGURATION ### spring: portal-datasource: @@ -17,28 +36,6 @@ spring: dialect: org.hibernate.dialect.PostgreSQL9Dialect ddl-auto: validate -### LOG LEVELS ### -logging: - level: - root: {{ default .Env.LOG_LEVEL_FRAMEWORK "ERROR" }} - org: {{ default .Env.LOG_LEVEL_FRAMEWORK "ERROR" }} - eu: - hbp: {{ default .Env.LOG_LEVEL "INFO" }} - -### EMBEDDED SERVER CONFIGURATION ### -server: - servlet: - contextPath: "/services" - port: 8080 - forward-headers-strategy: native - -### ENDPOINTS ### -endpoints: - enabled: true - health: - enabled: true - endpoint: /health - sensitive: false ### EXTERNAL SERVICES ### services: @@ -48,24 +45,21 @@ services: galaxy: galaxyUrl: {{ default .Env.GALAXY_URL "http://localhost:8090/" }} - galaxyContext: {{ default .Env.GALAXY_CONTEXT "nativeGalaxy/workflows/list" }} + galaxyContext: "nativeGalaxy/workflows/list" galaxyApiKey: {{ .Env.GALAXY_API_KEY }} galaxyUsername: {{ default .Env.GALAXY_USERNAME "admin" }} galaxyPassword: {{ default .Env.GALAXY_PASSWORD "password" }} -### Authentication ### -authentication: - enabled: {{ default .Env.AUTHENTICATION "1" }} -### Keycloak ### +### KEYCLOAK ### keycloak: enabled: true auth-server-url: {{ .Env.KEYCLOAK_AUTH_URL }} realm: {{ .Env.KEYCLOAK_REALM }} - resource: {{ .Env.CLIENT_ID }} + resource: {{ .Env.KEYCLOAK_CLIENT_ID }} enable-basic-auth: true credentials: - secret: {{ .Env.CLIENT_SECRET }} + secret: {{ .Env.KEYCLOAK_CLIENT_SECRET }} principal-attribute: "preferred_username" ### EXTERNAL FILES ### @@ -73,3 +67,20 @@ keycloak: files: pathologies_json: "file:/opt/portal/api/pathologies.json" disabledAlgorithms_json: "file:/opt/portal/api/disabledAlgorithms.json" + + +### EMBEDDED SERVER CONFIGURATION ### +server: + servlet: + contextPath: "/services" + port: 8080 + forward-headers-strategy: native + + +### ENDPOINTS ### +endpoints: + enabled: true + health: + enabled: true + endpoint: /health + sensitive: false \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java index 21a494b88..82f237726 100644 --- a/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java @@ -31,15 +31,17 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter @Value("#{'${authentication.enabled}'}") private boolean authenticationEnabled; + @Value("#{'${release_stage.production}'}") + private boolean deployedOnProduction; + @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); if (authenticationEnabled) { - // Used for development with authentication turned on. - // Should not be enabled on production. - http.csrf().disable(); + if(!deployedOnProduction) + http.csrf().disable(); http.authorizeRequests() .antMatchers( diff --git a/src/main/java/eu/hbp/mip/controllers/ExperimentAPI.java b/src/main/java/eu/hbp/mip/controllers/ExperimentAPI.java index 78f6032e0..21669bf94 100644 --- a/src/main/java/eu/hbp/mip/controllers/ExperimentAPI.java +++ b/src/main/java/eu/hbp/mip/controllers/ExperimentAPI.java @@ -2,7 +2,6 @@ package eu.hbp.mip.controllers; import eu.hbp.mip.models.DTOs.ExperimentDTO; -import eu.hbp.mip.services.ActiveUserService; import eu.hbp.mip.services.ExperimentService; import eu.hbp.mip.utils.JsonConverters; import io.swagger.annotations.Api; @@ -76,15 +75,6 @@ public class ExperimentAPI { } - @ApiOperation(value = "Create a transient experiment", response = ExperimentDTO.class) - @RequestMapping(value = "/transient", method = RequestMethod.POST) - public ResponseEntity<String> createTransientExperiment(Authentication authentication, @RequestBody ExperimentDTO experimentDTO) { - experimentDTO = experimentService.createTransientExperiment(authentication, experimentDTO, "(POST) /experiments/transient"); - - return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); - } - - @ApiOperation(value = "Update an experiment", response = ExperimentDTO.class) @RequestMapping(value = "/{uuid}", method = RequestMethod.PATCH) public ResponseEntity<String> updateExperiment(@RequestBody ExperimentDTO experimentDTO, @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { @@ -100,4 +90,13 @@ public class ExperimentAPI { experimentService.deleteExperiment(uuid, "(DELETE) /experiments/{uuid}"); return new ResponseEntity<>(HttpStatus.OK); } + + + @ApiOperation(value = "Create a transient experiment", response = ExperimentDTO.class) + @RequestMapping(value = "/transient", method = RequestMethod.POST) + public ResponseEntity<String> createTransientExperiment(Authentication authentication, @RequestBody ExperimentDTO experimentDTO) { + experimentDTO = experimentService.createTransientExperiment(authentication, experimentDTO, "(POST) /experiments/transient"); + + return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); + } } \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 30c82a447..076ed3b59 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,5 +1,24 @@ # Configuration for development purposes +### LOG LEVELS ### +logging: + level: + root: "ERROR" + org: "ERROR" + eu: + hbp: "DEBUG" + + +### AUTHENTICATION ### +authentication: + enabled: true + + +### RELEASE STAGE ### +release_stage: + production: false + + ### DATABASE CONFIGURATION ### spring: portal-datasource: @@ -17,28 +36,6 @@ spring: dialect: org.hibernate.dialect.PostgreSQL9Dialect ddl-auto: validate -### LOG LEVELS ### -logging: - level: - root: "ERROR" - org: "ERROR" - eu: - hbp: "DEBUG" - -### EMBEDDED SERVER CONFIGURATION ### -server: - servlet: - contextPath: "/services" - port: 8080 - forward-headers-strategy: native - -### ENDPOINTS ### -endpoints: - enabled: true - health: - enabled: true - endpoint: "/health" - sensitive: false ### EXTERNAL SERVICES ### services: @@ -52,11 +49,8 @@ services: galaxyUsername: "admin" galaxyPassword: "password" -### Authentication ### -authentication: - enabled: true -### Keycloak ### +### KEYCLOAK ### keycloak: enabled: true auth-server-url: "http://127.0.0.1/auth" @@ -67,8 +61,26 @@ keycloak: secret: "dae83a6b-c769-4186-8383-f0984c6edf05" principal-attribute: "preferred_username" + ### EXTERNAL FILES ### # Files are loaded from the resources files: pathologies_json: "classPath:/pathologies.json" disabledAlgorithms_json: "classPath:/disabledAlgorithms.json" + + +### EMBEDDED SERVER CONFIGURATION ### +server: + servlet: + contextPath: "/services" + port: 8080 + forward-headers-strategy: native + + +### ENDPOINTS ### +endpoints: + enabled: true + health: + enabled: true + endpoint: "/health" + sensitive: false \ No newline at end of file -- GitLab From d8351d20ffb85dfe3b32bb342cd6b69d2d19f7b7 Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Wed, 18 Nov 2020 06:20:19 -0800 Subject: [PATCH 33/52] Removed uneccessary dependency. --- pom.xml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/pom.xml b/pom.xml index 60a5d582c..bc18ad55a 100644 --- a/pom.xml +++ b/pom.xml @@ -189,7 +189,6 @@ <artifactId>aspectjweaver</artifactId> <version>${aspectjweaver.version}</version> </dependency> - <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> @@ -221,42 +220,32 @@ <artifactId>blend4j</artifactId> <version>0.2.0</version> </dependency> - <dependency> <groupId>com.squareup.retrofit2</groupId> <artifactId>retrofit</artifactId> <version>2.9.0</version> </dependency> - <!-- https://mvnrepository.com/artifact/com.squareup.retrofit2/converter-gson --> <dependency> <groupId>com.squareup.retrofit2</groupId> <artifactId>converter-gson</artifactId> <version>2.9.0</version> </dependency> - <!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/logging-interceptor --> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>logging-interceptor</artifactId> </dependency> - <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.11.0</version> </dependency> - <dependency> <groupId>com.google.code.svenson</groupId> <artifactId>svenson</artifactId> <version>1.5.8</version> </dependency> - <dependency> - <groupId>eu.hbp.mip</groupId> - <artifactId>portal-backend</artifactId> - <version>4.0.0</version> - </dependency> </dependencies> <build> -- GitLab From 041681b27cc018a0cf95e8842e1751b660eb8482 Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Wed, 18 Nov 2020 08:02:28 -0800 Subject: [PATCH 34/52] XSRF protection added. --- .../configurations/SecurityConfiguration.java | 52 ++++++++++++++++--- src/main/resources/application.yml | 2 +- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java index 82f237726..4e2905945 100644 --- a/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java @@ -14,11 +14,22 @@ import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; +import org.springframework.security.web.csrf.CsrfFilter; +import org.springframework.security.web.csrf.CsrfToken; +import org.springframework.security.web.csrf.CsrfTokenRepository; +import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.util.WebUtils; +import javax.servlet.Filter; +import javax.servlet.FilterChain; import javax.servlet.ServletException; +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; @Controller @@ -39,10 +50,6 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter super.configure(http); if (authenticationEnabled) { - - if(!deployedOnProduction) - http.csrf().disable(); - http.authorizeRequests() .antMatchers( "/sso/login", @@ -53,11 +60,44 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter } else { http.antMatcher("/**") .authorizeRequests() - .antMatchers("/**").permitAll() - .and().csrf().disable(); + .antMatchers("/**").permitAll(); + } + + if (!deployedOnProduction) { + // If deployed for development, csrf can be disabled + http.csrf().disable(); + } else { + http.csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository()) + .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class); } } + private Filter csrfHeaderFilter() { + return new OncePerRequestFilter() { + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); + if (csrf != null) { + Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN"); + String token = csrf.getToken(); + if (cookie == null || token != null && !token.equals(cookie.getValue())) { + cookie = new Cookie("XSRF-TOKEN", token); + cookie.setPath("/"); + response.addCookie(cookie); + } + } + filterChain.doFilter(request, response); + } + }; + } + + private CsrfTokenRepository csrfTokenRepository() { + HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository(); + repository.setHeaderName("X-XSRF-TOKEN"); + return repository; + } + @Autowired private HttpServletRequest request; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 076ed3b59..a62d819f6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,7 +16,7 @@ authentication: ### RELEASE STAGE ### release_stage: - production: false + production: true ### DATABASE CONFIGURATION ### -- GitLab From 7482e7f58c19e9489ed7d2dc71879de48336177b Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Thu, 19 Nov 2020 18:22:00 +0200 Subject: [PATCH 35/52] Removing deployed on production env variable, not needed. --- docker/README.md | 4 ---- docker/config/application.tmpl | 5 ----- .../configurations/SecurityConfiguration.java | 18 +++++------------- src/main/resources/application.yml | 5 ----- 4 files changed, 5 insertions(+), 27 deletions(-) diff --git a/docker/README.md b/docker/README.md index eb3c76498..9636ff324 100644 --- a/docker/README.md +++ b/docker/README.md @@ -16,10 +16,6 @@ To use this image, you need a running instance of PostgreSQL and to configure th * AUTHENTICATION: true for production, false for development. -### RELEASE STAGE ### -* PRODUCTION: Deployed on production? (True/False) Default is True. - - ### DATABASE CONFIGURATION ### * PORTAL_DB_URL: JDBC URL to connect to the portal database, default value is "jdbc:postgresql://127.0.0.1:5432/portal". * PORTAL_DB_SCHEMA: Database schema, default value is "public". diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index 423baccff..5859f78a8 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -14,11 +14,6 @@ authentication: enabled: {{ default .Env.AUTHENTICATION "true" }} -### RELEASE STAGE ### -release_stage: - production: {{ default .Env.PRODUCTION "true" }} - - ### DATABASE CONFIGURATION ### spring: portal-datasource: diff --git a/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java index 4e2905945..59929212b 100644 --- a/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java @@ -42,9 +42,6 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter @Value("#{'${authentication.enabled}'}") private boolean authenticationEnabled; - @Value("#{'${release_stage.production}'}") - private boolean deployedOnProduction; - @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); @@ -56,19 +53,14 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter "/v2/api-docs", "/swagger-ui/**", "/swagger-resources/**" // Swagger URLs ).permitAll() .antMatchers("/galaxy*", "/galaxy/*").hasRole("DATA MANAGER") - .anyRequest().hasRole("RESEARCHER"); + .anyRequest().hasRole("RESEARCHER") + .and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository()) + .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class); } else { http.antMatcher("/**") .authorizeRequests() - .antMatchers("/**").permitAll(); - } - - if (!deployedOnProduction) { - // If deployed for development, csrf can be disabled - http.csrf().disable(); - } else { - http.csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository()) - .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class); + .antMatchers("/**").permitAll() + .and().csrf().disable(); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a62d819f6..84053f912 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -14,11 +14,6 @@ authentication: enabled: true -### RELEASE STAGE ### -release_stage: - production: true - - ### DATABASE CONFIGURATION ### spring: portal-datasource: -- GitLab From 92d603a6f208f42eaa900c1267876254c03bbddc Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Thu, 19 Nov 2020 10:40:26 -0800 Subject: [PATCH 36/52] Remove article from database.Made GET /experiments return only my experiments or the shared and wont print the result of the experiment. --- pom.xml | 6 +- .../eu/hbp/mip/controllers/ExperimentAPI.java | 4 +- .../eu/hbp/mip/models/DAOs/ExperimentDAO.java | 14 ++--- .../eu/hbp/mip/models/DTOs/ExperimentDTO.java | 10 ++- .../hbp/mip/services/ExperimentService.java | 62 +++++++++++-------- .../services/ExperimentSpecifications.java | 39 ++++++++++-- .../java/eu/hbp/mip/utils/ClaimUtils.java | 11 ++-- src/main/resources/application.yml | 2 +- .../migration/V7_0__NewDatabaseStructure.sql | 5 +- 9 files changed, 99 insertions(+), 54 deletions(-) diff --git a/pom.xml b/pom.xml index 60a5d582c..a9f07d9c2 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 78f6032e0..886c59232 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 1cf4864bd..f43c34960 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 8c6073a1f..5707b75d0 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 ed5c8edd8..a79db1016 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 45169c09d..cfc6a5769 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 4e4f95c9d..5eb90a2d0 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 30c82a447..366f1c340 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 5e1bcc6fb..a1c728e96 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"; -- GitLab From f20948890ed0a88e1d2b275942f04eec78edd8ea Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Sun, 29 Nov 2020 04:02:55 -0800 Subject: [PATCH 37/52] Application.yml can always be included. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b9da79480..5d8467960 100644 --- a/pom.xml +++ b/pom.xml @@ -259,7 +259,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> -- GitLab From c570c5b010475ec61129ad5710e9f5d6f3417c34 Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Sun, 29 Nov 2020 04:12:32 -0800 Subject: [PATCH 38/52] CORS Filter added when authentication is disabled. --- .../configurations/SecurityConfiguration.java | 8 +++--- .../java/eu/hbp/mip/utils/CORSFilter.java | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 src/main/java/eu/hbp/mip/utils/CORSFilter.java diff --git a/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java index 59929212b..ea5cf7860 100644 --- a/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java @@ -1,5 +1,6 @@ package eu.hbp.mip.configurations; +import eu.hbp.mip.utils.CORSFilter; import org.keycloak.adapters.KeycloakConfigResolver; import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; import org.keycloak.adapters.springsecurity.KeycloakConfiguration; @@ -12,6 +13,7 @@ import org.springframework.security.config.annotation.authentication.builders.Au import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; import org.springframework.security.core.session.SessionRegistryImpl; +import org.springframework.security.web.access.channel.ChannelProcessingFilter; import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; import org.springframework.security.web.csrf.CsrfFilter; @@ -20,12 +22,11 @@ import org.springframework.security.web.csrf.CsrfTokenRepository; import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.filter.CorsFilter; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.WebUtils; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; +import javax.servlet.*; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -57,6 +58,7 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter .and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository()) .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class); } else { + http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class); http.antMatcher("/**") .authorizeRequests() .antMatchers("/**").permitAll() diff --git a/src/main/java/eu/hbp/mip/utils/CORSFilter.java b/src/main/java/eu/hbp/mip/utils/CORSFilter.java new file mode 100644 index 000000000..1a0696fa5 --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/CORSFilter.java @@ -0,0 +1,25 @@ +package eu.hbp.mip.utils; + + +import javax.servlet.*; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + + +/** + * CORS Filter used only for development. + * + * Allows requests from all possible origins. + */ +public class CORSFilter implements Filter { + @Override + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { + HttpServletResponse response = (HttpServletResponse) res; + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT"); + response.setHeader("Access-Control-Max-Age", "3600"); + response.setHeader("Access-Control-Allow-Headers", "*"); + response.setHeader("Access-Control-Request-Headers", "*"); + chain.doFilter(req, res); + } +} \ No newline at end of file -- GitLab From 95dfe1d7bafbb7ed083a0faa19ee565905ade865 Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Sun, 29 Nov 2020 04:42:10 -0800 Subject: [PATCH 39/52] Fix for CORS allowed requests. --- src/main/java/eu/hbp/mip/utils/CORSFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/eu/hbp/mip/utils/CORSFilter.java b/src/main/java/eu/hbp/mip/utils/CORSFilter.java index 1a0696fa5..7d77b9022 100644 --- a/src/main/java/eu/hbp/mip/utils/CORSFilter.java +++ b/src/main/java/eu/hbp/mip/utils/CORSFilter.java @@ -16,7 +16,7 @@ public class CORSFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; response.setHeader("Access-Control-Allow-Origin", "*"); - response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT"); + response.setHeader("Access-Control-Allow-Methods", "GET,POST,PATCH,DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "*"); response.setHeader("Access-Control-Request-Headers", "*"); -- GitLab From 3c08abe796f42da2eb244f89abf881406b04a991 Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Thu, 3 Dec 2020 04:51:33 -0800 Subject: [PATCH 40/52] Added badrequest in case algorithm type is null and fix the convertion of the json that exareme provides to a list of results. --- src/main/java/eu/hbp/mip/services/ExperimentService.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index a79db1016..eb4001665 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -169,6 +169,11 @@ public class ExperimentService { // Get the type and name of algorithm String algorithmType = experimentDTO.getAlgorithmDetails().getType(); + if(algorithmType == null){ + Logging.LogUserAction(user.getUsername(), endpoint, "Please provide algorithm type."); + throw new BadRequestException("Please provide algorithm type."); + } + algorithmParametersLogging(experimentDTO, endpoint); if (authenticationIsEnabled) { @@ -577,7 +582,8 @@ public class ExperimentService { Logging.LogExperimentAction(experimentDTO.getName(), experimentDTO.getUuid(), "Algorithm finished with code: " + code); // Results are stored in the experiment object - List<ExperimentDTO.ResultDTO> resultDTOS = JsonConverters.convertJsonStringToObject(String.valueOf(results), new ArrayList<ExperimentDTO.ResultDTO>().getClass()); + ExperimentDTO experimentDTOWithOnlyResult = JsonConverters.convertJsonStringToObject(String.valueOf(results), ExperimentDTO.class); + List<ExperimentDTO.ResultDTO> resultDTOS = experimentDTOWithOnlyResult.getResult(); return new ExaremeResult(code, resultDTOS); } -- GitLab From 1b55430100fc2924a65a38f4910878adeb332900 Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Thu, 3 Dec 2020 05:17:02 -0800 Subject: [PATCH 41/52] Removed the algorithm name from experimentDTO so it wont be required or will return algorithm name twice but the algorithm name still exists in the database to help with the filtering on the GET /experiments request. --- .../eu/hbp/mip/models/DAOs/ExperimentDAO.java | 3 +-- .../eu/hbp/mip/models/DTOs/ExperimentDTO.java | 18 +++---------- .../hbp/mip/services/ExperimentService.java | 26 +++++++++---------- 3 files changed, 17 insertions(+), 30 deletions(-) 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 f43c34960..cd3f50757 100644 --- a/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java +++ b/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java @@ -7,7 +7,6 @@ import eu.hbp.mip.models.DTOs.AlgorithmDTO; import eu.hbp.mip.models.DTOs.ExperimentDTO; import eu.hbp.mip.utils.JsonConverters; import io.swagger.annotations.ApiModel; -import org.svenson.JSONParser; import javax.persistence.*; import java.util.*; @@ -117,7 +116,7 @@ public class ExperimentDAO { public ExperimentDTO convertToDTO(boolean includeResult) { ExperimentDTO experimentDTO = new ExperimentDTO(); - experimentDTO.setAlgorithmDetails(JsonConverters.convertJsonStringToObject(this.algorithmDetails, AlgorithmDTO.class)); + experimentDTO.setAlgorithm(JsonConverters.convertJsonStringToObject(this.algorithmDetails, AlgorithmDTO.class)); experimentDTO.setCreated(this.created); experimentDTO.setUpdated(this.updated); experimentDTO.setFinished(this.finished); 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 5707b75d0..5f554dbe6 100644 --- a/src/main/java/eu/hbp/mip/models/DTOs/ExperimentDTO.java +++ b/src/main/java/eu/hbp/mip/models/DTOs/ExperimentDTO.java @@ -3,10 +3,8 @@ 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) @@ -23,29 +21,19 @@ public class ExperimentDTO { private List<ExperimentDTO.ResultDTO> result; private ExperimentDAO.Status status; - private String algorithm; - private AlgorithmDTO algorithmDetails; + private AlgorithmDTO algorithm; public ExperimentDTO() { } - public String getAlgorithm() { + public AlgorithmDTO getAlgorithm() { return algorithm; } - public void setAlgorithm(String algorithm) { + public void setAlgorithm(AlgorithmDTO algorithm) { this.algorithm = algorithm; } - public AlgorithmDTO getAlgorithmDetails() { - return algorithmDetails; - } - - public void setAlgorithmDetails(AlgorithmDTO algorithmDetails) { - this.algorithmDetails = algorithmDetails; - this.algorithm = algorithmDetails.getName(); - } - public UUID getUuid() { return uuid; } diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index eb4001665..f92efef0d 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -167,7 +167,7 @@ public class ExperimentService { checkPostExperimentProperInput(experimentDTO, endpoint); // Get the type and name of algorithm - String algorithmType = experimentDTO.getAlgorithmDetails().getType(); + String algorithmType = experimentDTO.getAlgorithm().getType(); if(algorithmType == null){ Logging.LogUserAction(user.getUsername(), endpoint, "Please provide algorithm type."); @@ -207,10 +207,10 @@ public class ExperimentService { // Get the parameters List<AlgorithmDTO.AlgorithmParamDTO> algorithmParameters - = experimentDTO.getAlgorithmDetails().getParameters(); + = experimentDTO.getAlgorithm().getParameters(); // Get the type and name of algorithm - String algorithmName = experimentDTO.getAlgorithmDetails().getName(); + String algorithmName = experimentDTO.getAlgorithm().getName(); if (!allowedTransientAlgorithms(algorithmName)) { Logging.LogUserAction(user.getUsername(), endpoint, @@ -356,7 +356,7 @@ public class ExperimentService { throw new BadRequestException("CreatedBy is not editable."); } - if (experimentDTO.getAlgorithmDetails() != null) { + if (experimentDTO.getAlgorithm() != null) { Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "AlgorithmDetails is not editable."); throw new BadRequestException("AlgorithmDetails is not editable."); } @@ -379,9 +379,9 @@ public class ExperimentService { private void algorithmParametersLogging(ExperimentDTO experimentDTO, String endpoint) { UserDAO user = activeUserService.getActiveUser(); - String algorithmName = experimentDTO.getAlgorithm(); + String algorithmName = experimentDTO.getAlgorithm().getName(); StringBuilder parametersLogMessage = new StringBuilder(", Parameters:\n"); - experimentDTO.getAlgorithmDetails().getParameters().forEach( + experimentDTO.getAlgorithm().getParameters().forEach( params -> parametersLogMessage .append(" ") .append(params.getLabel()) @@ -401,7 +401,7 @@ public class ExperimentService { private String getDatasetFromExperimentParameters(ExperimentDTO experimentDTO, String endpoint) { String experimentDatasets = null; - for (AlgorithmDTO.AlgorithmParamDTO parameter : experimentDTO.getAlgorithmDetails().getParameters()) { + for (AlgorithmDTO.AlgorithmParamDTO parameter : experimentDTO.getAlgorithm().getParameters()) { if (parameter.getLabel().equals("dataset")) { experimentDatasets = parameter.getValue(); break; @@ -454,8 +454,8 @@ public class ExperimentService { ExperimentDAO experimentDAO = new ExperimentDAO(); experimentDAO.setUuid(UUID.randomUUID()); experimentDAO.setCreatedBy(user); - experimentDAO.setAlgorithmDetails(JsonConverters.convertObjectToJsonString(experimentDTO.getAlgorithmDetails())); - experimentDAO.setAlgorithm(experimentDTO.getAlgorithm()); + experimentDAO.setAlgorithmDetails(JsonConverters.convertObjectToJsonString(experimentDTO.getAlgorithm())); + experimentDAO.setAlgorithm(experimentDTO.getAlgorithm().getName()); experimentDAO.setName(experimentDTO.getName()); experimentDAO.setStatus(ExperimentDAO.Status.pending); @@ -520,11 +520,11 @@ public class ExperimentService { Logging.LogUserAction(user.getUsername(), endpoint, "Created experiment with uuid :" + experimentDAO.getUuid()); // Run the 1st algorithm from the list - String algorithmName = experimentDTO.getAlgorithmDetails().getName(); + String algorithmName = experimentDTO.getAlgorithm().getName(); // Get the parameters List<AlgorithmDTO.AlgorithmParamDTO> algorithmParameters - = experimentDTO.getAlgorithmDetails().getParameters(); + = experimentDTO.getAlgorithm().getParameters(); String body = gson.toJson(algorithmParameters); String url = queryExaremeUrl + "/" + algorithmName; @@ -606,11 +606,11 @@ public class ExperimentService { // Run the 1st algorithm from the list - String workflowId = experimentDTO.getAlgorithmDetails().getName(); + String workflowId = experimentDTO.getAlgorithm().getName(); // Get the parameters List<AlgorithmDTO.AlgorithmParamDTO> algorithmParameters - = experimentDTO.getAlgorithmDetails().getParameters(); + = experimentDTO.getAlgorithm().getParameters(); // Convert the parameters to workflow parameters HashMap<String, String> algorithmParamsIncludingEmpty = new HashMap<>(); -- GitLab From d76db5b90f577e8869a2b2d0ee9d878c0fd7a9ae Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Tue, 8 Dec 2020 02:55:19 -0800 Subject: [PATCH 42/52] Updated database calls to be case insensitive. --- .../java/eu/hbp/mip/services/ExperimentSpecifications.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java b/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java index cfc6a5769..e5b51a692 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java @@ -27,7 +27,7 @@ public class ExperimentSpecifications { regExp = (name.contains("%") ? name : name + "%"); } - return cb.like(root.get("name"), this.regExp); + return cb.like(cb.lower(root.get("name")), this.regExp.toLowerCase()); } } @@ -44,7 +44,7 @@ public class ExperimentSpecifications { return cb.isTrue(cb.literal(true)); } - return cb.equal(root.get("algorithm"), this.algorithm); + return cb.equal(cb.lower(root.get("algorithm")), this.algorithm.toLowerCase()); } } -- GitLab From bc44206bffc73f6f31078fe4fe9518f1553fdc2f Mon Sep 17 00:00:00 2001 From: kfilippopolitis <kostasfilippop@gmail.com> Date: Tue, 8 Dec 2020 03:25:44 -0800 Subject: [PATCH 43/52] Documentation about the algorithm name column that will be stored in the database. --- src/main/java/eu/hbp/mip/services/ExperimentService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index f92efef0d..6039d577f 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -447,6 +447,8 @@ public class ExperimentService { * * @param experimentDTO is the experiment information to inserted in the database * @return the experiment information that was inserted into the database + * @Note In the database there will be stored Algorithm Details that is the whole information about the algorithm + * and an Algorithm column that is required for the filtering with algorithm name in the GET /experiments. */ private ExperimentDAO createExperimentInTheDatabase(ExperimentDTO experimentDTO, String endpoint) { UserDAO user = activeUserService.getActiveUser(); -- GitLab From d9d4703dadcfae6d41e5f5b1ab2f46d16befd1d2 Mon Sep 17 00:00:00 2001 From: kostas <kostasfilippop@gmail.com> Date: Fri, 11 Dec 2020 06:52:50 -0800 Subject: [PATCH 44/52] Made so the transient endpoint will work with any algorithm. --- build.sh | 2 +- .../eu/hbp/mip/models/DAOs/ExperimentDAO.java | 23 ------------------- .../eu/hbp/mip/models/DTOs/ExperimentDTO.java | 6 ++--- .../hbp/mip/services/ExperimentService.java | 13 ----------- 4 files changed, 4 insertions(+), 40 deletions(-) diff --git a/build.sh b/build.sh index 458b5c085..6a18d98ec 100755 --- a/build.sh +++ b/build.sh @@ -26,7 +26,7 @@ else DOCKER="sudo docker" fi -IMAGE="thanasulas/portal-backend" +IMAGE="kfilippopolitis/portal-backend" VCS_REF=$(git describe --tags --dirty) VERSION=$(git describe --tags --dirty) 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 cd3f50757..f634c77a2 100644 --- a/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java +++ b/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java @@ -84,29 +84,6 @@ public class ExperimentDAO { success } - public enum Type { - WARNING("text/plain+warning"), - USER_WARNING("text/plain+user_error"), - HIGHCHARTS("application/vnd.highcharts+json"), - JSON("application/json"), - JSONBTREE("application/binary-tree+json"), - PFA("application/pfa+json"), - JSONDATA("application/vnd.dataresource+json"), - HTML("text/html"), - TEXT("text/plain"); - - private String type; - - //Constructor to initialize the instance variable - Type(String type) { - this.type = type; - } - - public String getType() { - return this.type; - } - } - public ExperimentDAO() { /* * Empty constructor is needed by Hibernate 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 5f554dbe6..a1c6e916c 100644 --- a/src/main/java/eu/hbp/mip/models/DTOs/ExperimentDTO.java +++ b/src/main/java/eu/hbp/mip/models/DTOs/ExperimentDTO.java @@ -117,7 +117,7 @@ public class ExperimentDTO { public static class ResultDTO { private Object data; - private ExperimentDAO.Type type; + private Object type; public Object getData() { return this.data; @@ -127,11 +127,11 @@ public class ExperimentDTO { this.data = data; } - public ExperimentDAO.Type getType() { + public Object getType() { return type; } - public void setType(ExperimentDAO.Type type) { + public void setType(Object type) { this.type = type; } } diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index 6039d577f..56861903c 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -212,12 +212,6 @@ public class ExperimentService { // Get the type and name of algorithm String algorithmName = experimentDTO.getAlgorithm().getName(); - if (!allowedTransientAlgorithms(algorithmName)) { - Logging.LogUserAction(user.getUsername(), endpoint, - "Not proper algorithm."); - throw new BadRequestException("Please provide proper algorithm."); - } - algorithmParametersLogging(experimentDTO, endpoint); if (authenticationIsEnabled) { @@ -333,13 +327,6 @@ public class ExperimentService { } } - private boolean allowedTransientAlgorithms(String algorithmName) { - List<String> properAlgorithms = new ArrayList<>(); - properAlgorithms.add("MULTIPLE_HISTOGRAMS"); - properAlgorithms.add("DESCRIPTIVE_STATS"); - return properAlgorithms.contains(algorithmName); - } - private void verifyPatchExperimentNonEditableFields(String uuid, ExperimentDTO experimentDTO, ExperimentDAO experimentDAO, String endpoint) { if (experimentDTO.getUuid() != null ) { Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "Uuid is not editable."); -- GitLab From 49e1b19e6ae81d80cd3aa26a80b9d3e3f2828c5e Mon Sep 17 00:00:00 2001 From: kostas <kostasfilippop@gmail.com> Date: Thu, 17 Dec 2020 05:27:24 -0800 Subject: [PATCH 45/52] Update the ClaimUtils according to the keycloak refactoring. --- .../configurations/GalaxyAuthentication.java | 15 +- .../configurations/SecurityConfiguration.java | 10 +- .../eu/hbp/mip/controllers/ActiveUserAPI.java | 18 +- .../eu/hbp/mip/controllers/AlgorithmsAPI.java | 75 ++-- .../eu/hbp/mip/controllers/ExperimentAPI.java | 29 +- .../hbp/mip/controllers/PathologiesAPI.java | 28 +- .../hbp/mip/services/ActiveUserService.java | 14 +- .../hbp/mip/services/ExperimentService.java | 360 +++++++++--------- .../java/eu/hbp/mip/utils/ClaimUtils.java | 67 ++-- .../mip/utils/{Logging.java => Logger.java} | 17 +- 10 files changed, 315 insertions(+), 318 deletions(-) rename src/main/java/eu/hbp/mip/utils/{Logging.java => Logger.java} (53%) diff --git a/src/main/java/eu/hbp/mip/configurations/GalaxyAuthentication.java b/src/main/java/eu/hbp/mip/configurations/GalaxyAuthentication.java index 41888d3ea..e8a5df4bc 100644 --- a/src/main/java/eu/hbp/mip/configurations/GalaxyAuthentication.java +++ b/src/main/java/eu/hbp/mip/configurations/GalaxyAuthentication.java @@ -3,8 +3,7 @@ package eu.hbp.mip.configurations; import com.google.gson.Gson; import com.google.gson.JsonObject; import eu.hbp.mip.services.ActiveUserService; -import eu.hbp.mip.utils.Logging; -import org.springframework.beans.factory.annotation.Autowired; +import eu.hbp.mip.utils.Logger; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -19,8 +18,7 @@ import java.util.Base64; @RestController public class GalaxyAuthentication { - @Autowired - private ActiveUserService activeUserService; + private final ActiveUserService activeUserService; @Value("#{'${services.galaxy.galaxyUsername:admin}'}") private String galaxyUsername; @@ -31,20 +29,25 @@ public class GalaxyAuthentication { @Value("#{'${services.galaxy.galaxpathoyContext:nativeGalaxy}'}") private String galaxyContext; + public GalaxyAuthentication(ActiveUserService activeUserService) { + this.activeUserService = activeUserService; + } + /** * Get Galaxy Reverse Proxy basic access token. * * @return Return a @{@link ResponseEntity} with the token. */ @RequestMapping(path = "/galaxy", method = RequestMethod.GET, produces = "application/json") - @PreAuthorize("hasRole('Data Manager')") + @PreAuthorize("hasRole('WORKFLOW_ADMIN')") @ResponseStatus(value = HttpStatus.OK) public ResponseEntity getGalaxyConfiguration() { + Logger logger = new Logger(activeUserService.getActiveUser().getUsername(), "(GET) /user/galaxy"); String stringEncoded = Base64.getEncoder().encodeToString((galaxyUsername + ":" + galaxyPassword).getBytes()); JsonObject object = new JsonObject(); object.addProperty("authorization", stringEncoded); object.addProperty("context", galaxyContext); - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /user/galaxy", "Successfully Loaded galaxy information."); + logger.LogUserAction("Successfully Loaded galaxy information."); return ResponseEntity.ok(new Gson().toJson(object)); } diff --git a/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java index ea5cf7860..2b2e549d4 100644 --- a/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java @@ -22,7 +22,6 @@ import org.springframework.security.web.csrf.CsrfTokenRepository; import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.filter.CorsFilter; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.WebUtils; @@ -43,6 +42,10 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter @Value("#{'${authentication.enabled}'}") private boolean authenticationEnabled; + public SecurityConfiguration(HttpServletRequest request) { + this.request = request; + } + @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); @@ -54,7 +57,7 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter "/v2/api-docs", "/swagger-ui/**", "/swagger-resources/**" // Swagger URLs ).permitAll() .antMatchers("/galaxy*", "/galaxy/*").hasRole("DATA MANAGER") - .anyRequest().hasRole("RESEARCHER") + .antMatchers("/**").authenticated() .and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository()) .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class); } else { @@ -92,8 +95,7 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter return repository; } - @Autowired - private HttpServletRequest request; + private final HttpServletRequest request; @GetMapping(value = "/logout") public String logout() throws ServletException { diff --git a/src/main/java/eu/hbp/mip/controllers/ActiveUserAPI.java b/src/main/java/eu/hbp/mip/controllers/ActiveUserAPI.java index 7e5209b75..aca62c4b6 100644 --- a/src/main/java/eu/hbp/mip/controllers/ActiveUserAPI.java +++ b/src/main/java/eu/hbp/mip/controllers/ActiveUserAPI.java @@ -2,10 +2,9 @@ package eu.hbp.mip.controllers; import eu.hbp.mip.models.DAOs.UserDAO; import eu.hbp.mip.services.ActiveUserService; -import eu.hbp.mip.utils.Logging; +import eu.hbp.mip.utils.Logger; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -21,14 +20,17 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @Api(value = "/activeUser") public class ActiveUserAPI { - @Autowired - private ActiveUserService activeUserService; + private final ActiveUserService activeUserService; + + public ActiveUserAPI(ActiveUserService activeUserService) { + this.activeUserService = activeUserService; + } @ApiOperation(value = "Get the active user", response = UserDAO.class) @RequestMapping(method = RequestMethod.GET) public ResponseEntity<UserDAO> getTheActiveUser(HttpServletResponse response) { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /activeUser", - "Loading the details of the activeUser"); + Logger logger = new Logger(activeUserService.getActiveUser().getUsername(), "(GET) /activeUser"); + logger.LogUserAction("Loading the details of the activeUser"); return ResponseEntity.ok(activeUserService.getActiveUser()); } @@ -36,8 +38,8 @@ public class ActiveUserAPI { @ApiOperation(value = "The active user agrees to the NDA", response = UserDAO.class) @RequestMapping(value = "/agreeNDA", method = RequestMethod.POST) public ResponseEntity<UserDAO> activeUserServiceAgreesToNDA(@RequestBody(required = false) UserDAO userDAO) { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), "(GET) /activeUser/agreeNDA", - "The user agreed to the NDA"); + Logger logger = new Logger(activeUserService.getActiveUser().getUsername(), "(GET) /activeUser/agreeNDA"); + logger.LogUserAction("The user agreed to the NDA"); return ResponseEntity.ok(activeUserService.agreeToNDA()); } diff --git a/src/main/java/eu/hbp/mip/controllers/AlgorithmsAPI.java b/src/main/java/eu/hbp/mip/controllers/AlgorithmsAPI.java index 001409d25..48500bb10 100644 --- a/src/main/java/eu/hbp/mip/controllers/AlgorithmsAPI.java +++ b/src/main/java/eu/hbp/mip/controllers/AlgorithmsAPI.java @@ -9,14 +9,13 @@ import com.google.gson.reflect.TypeToken; import eu.hbp.mip.controllers.galaxy.retrofit.RetroFitGalaxyClients; import eu.hbp.mip.controllers.galaxy.retrofit.RetrofitClientInstance; import eu.hbp.mip.models.DTOs.AlgorithmDTO; -import eu.hbp.mip.services.ActiveUserService; import eu.hbp.mip.models.galaxy.WorkflowDTO; +import eu.hbp.mip.services.ActiveUserService; import eu.hbp.mip.utils.CustomResourceLoader; import eu.hbp.mip.utils.HTTPUtil; -import eu.hbp.mip.utils.Logging; +import eu.hbp.mip.utils.Logger; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; import org.springframework.http.ResponseEntity; @@ -41,8 +40,7 @@ public class AlgorithmsAPI { private static final Gson gson = new Gson(); - @Autowired - private ActiveUserService activeUserService; + private final ActiveUserService activeUserService; @Value("#{'${services.exareme.algorithmsUrl}'}") private String exaremeAlgorithmsUrl; @@ -56,38 +54,40 @@ public class AlgorithmsAPI { @Value("#{'${files.disabledAlgorithms_json}'}") private String disabledAlgorithmsFilePath; + public AlgorithmsAPI(ActiveUserService activeUserService, CustomResourceLoader resourceLoader) { + this.activeUserService = activeUserService; + this.resourceLoader = resourceLoader; + } + @ApiOperation(value = "List all algorithms", response = String.class) @RequestMapping(method = RequestMethod.GET) public ResponseEntity<List<AlgorithmDTO>> getAlgorithms() { - String username = activeUserService.getActiveUser().getUsername(); - String endpoint = "(GET) /algorithms"; - Logging.LogUserAction(username, endpoint, "Executing..."); + Logger logger = new Logger(activeUserService.getActiveUser().getUsername(), "(GET) /algorithms"); + + logger.LogUserAction("Executing..."); - LinkedList<AlgorithmDTO> exaremeAlgorithms = getExaremeAlgorithms(); - Logging.LogUserAction(username, endpoint, "Loaded " + exaremeAlgorithms.size() + " exareme algorithms"); - LinkedList<AlgorithmDTO> galaxyAlgorithms = getGalaxyWorkflows(); - Logging.LogUserAction(username, endpoint, "Loaded " + galaxyAlgorithms.size() + " galaxy algorithms"); + LinkedList<AlgorithmDTO> exaremeAlgorithms = getExaremeAlgorithms(logger); + logger.LogUserAction("Loaded " + exaremeAlgorithms.size() + " exareme algorithms"); + LinkedList<AlgorithmDTO> galaxyAlgorithms = getGalaxyWorkflows(logger); + logger.LogUserAction("Loaded " + galaxyAlgorithms.size() + " galaxy algorithms"); LinkedList<AlgorithmDTO> algorithms = new LinkedList<>(); if (exaremeAlgorithms != null) { algorithms.addAll(exaremeAlgorithms); } else { - Logging.LogUserAction(username, endpoint, - "Getting exareme algorithms failed and returned null"); + logger.LogUserAction("Getting exareme algorithms failed and returned null"); } if (galaxyAlgorithms != null) { algorithms.addAll(galaxyAlgorithms); } else { - Logging.LogUserAction(username, endpoint, - "Getting galaxy workflows failed and returned null"); + logger.LogUserAction("Getting galaxy workflows failed and returned null"); } List<String> disabledAlgorithms = new ArrayList<>(); try { disabledAlgorithms = getDisabledAlgorithms(); } catch (IOException e) { - Logging.LogUserAction(username, endpoint, - "The disabled algorithms could not be loaded."); + logger.LogUserAction("The disabled algorithms could not be loaded."); } // Remove any disabled algorithm @@ -97,8 +97,7 @@ public class AlgorithmsAPI { allowedAlgorithms.add(algorithm); } } - Logging.LogUserAction(username, endpoint, - "Successfully listed " + allowedAlgorithms.size() + " algorithms"); + logger.LogUserAction("Successfully listed " + allowedAlgorithms.size() + " algorithms"); return ResponseEntity.ok(allowedAlgorithms); } @@ -107,10 +106,8 @@ public class AlgorithmsAPI { * * @return a list of AlgorithmDTOs or null if something fails */ - public LinkedList<AlgorithmDTO> getExaremeAlgorithms() { - String username = activeUserService.getActiveUser().getUsername(); - String endpoint = "(GET) /algorithms"; - LinkedList<AlgorithmDTO> algorithms = new LinkedList<>(); + public LinkedList<AlgorithmDTO> getExaremeAlgorithms(Logger logger) { + LinkedList<AlgorithmDTO> algorithms; // Get exareme algorithms try { StringBuilder response = new StringBuilder(); @@ -122,12 +119,11 @@ public class AlgorithmsAPI { }.getType() ); } catch (IOException e) { - Logging.LogUserAction(username, endpoint, "An exception occurred: " + e.getMessage()); + logger.LogUserAction("An exception occurred: " + e.getMessage()); return null; } - Logging.LogUserAction(username, endpoint, - "Completed, returned " + algorithms.size() + " algorithms."); + logger.LogUserAction("Completed, returned " + algorithms.size() + " algorithms."); return algorithms; } @@ -136,9 +132,7 @@ public class AlgorithmsAPI { * * @return a list of AlgorithmDTOs or null if something fails */ - public LinkedList<AlgorithmDTO> getGalaxyWorkflows() { - String username = activeUserService.getActiveUser().getUsername(); - String endpoint = "(GET) /algorithms"; + public LinkedList<AlgorithmDTO> getGalaxyWorkflows(Logger logger) { List<Workflow> workflowList; try { // Get all the workflows with the galaxy client @@ -147,7 +141,7 @@ public class AlgorithmsAPI { workflowList = new ArrayList<>(workflowsClient.getWorkflows()); } catch (Exception e) { - Logging.LogUserAction(username, endpoint, "Error when calling list galaxy workflows: " + e.getMessage()); + logger.LogUserAction("Error when calling list galaxy workflows: " + e.getMessage()); return null; } @@ -166,33 +160,31 @@ public class AlgorithmsAPI { } else { // Something unexpected happened String msgErr = gson.toJson(response.errorBody()); - Logging.LogUserAction(username, endpoint, "Error Response: " + msgErr); + logger.LogUserAction("Error Response: " + msgErr); return null; } } catch (Exception e) { - Logging.LogUserAction(username, endpoint, "An exception occurred: " + e.getMessage()); + logger.LogUserAction("An exception occurred: " + e.getMessage()); return null; } } - Logging.LogUserAction(username, endpoint, "Workflows fetched: " + workflows.size()); + logger.LogUserAction("Workflows fetched: " + workflows.size()); // Convert the workflows to algorithms LinkedList<AlgorithmDTO> algorithms = new LinkedList<>(); for (WorkflowDTO workflow : workflows) { - Logging.LogUserAction(username, endpoint, "Converting workflow: " + workflow); + logger.LogUserAction("Converting workflow: " + workflow); algorithms.add(workflow.convertToAlgorithmDTO()); - Logging.LogUserAction(username, endpoint, - "Converted algorithm: " + algorithms.get(algorithms.size() - 1)); + logger.LogUserAction("Converted algorithm: " + algorithms.get(algorithms.size() - 1)); } - Logging.LogUserAction(username, endpoint, "Completed!"); + logger.LogUserAction("Completed!"); return algorithms; } - @Autowired - private CustomResourceLoader resourceLoader; + private final CustomResourceLoader resourceLoader; /** * Fetches the disabled algorithms from a .json file @@ -204,11 +196,10 @@ public class AlgorithmsAPI { Resource resource = resourceLoader.getResource(disabledAlgorithmsFilePath); - List<String> response = gson.fromJson(convertInputStreamToString( + return gson.fromJson(convertInputStreamToString( resource.getInputStream()), new TypeToken<List<String>>() { }.getType() ); - return response; } } diff --git a/src/main/java/eu/hbp/mip/controllers/ExperimentAPI.java b/src/main/java/eu/hbp/mip/controllers/ExperimentAPI.java index 5c002c0b8..e5d0b04c5 100644 --- a/src/main/java/eu/hbp/mip/controllers/ExperimentAPI.java +++ b/src/main/java/eu/hbp/mip/controllers/ExperimentAPI.java @@ -2,8 +2,10 @@ package eu.hbp.mip.controllers; import eu.hbp.mip.models.DTOs.ExperimentDTO; +import eu.hbp.mip.services.ActiveUserService; import eu.hbp.mip.services.ExperimentService; import eu.hbp.mip.utils.JsonConverters; +import eu.hbp.mip.utils.Logger; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; @@ -27,14 +29,18 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; public class ExperimentAPI { private final ExperimentService experimentService; - - public ExperimentAPI(ExperimentService experimentService) { + private final ActiveUserService activeUserService; + public ExperimentAPI( + ExperimentService experimentService, + ActiveUserService activeUserService + ) { this.experimentService = experimentService; + this.activeUserService = activeUserService; } @ApiOperation(value = "Get experiments", response = Map.class, responseContainer = "List") @RequestMapping(method = RequestMethod.GET) - public ResponseEntity<String> getExperiments( + public ResponseEntity<String> getExperiments(Authentication authentication, @RequestParam(name = "name", required = false) String name, @RequestParam(name = "algorithm", required = false) String algorithm, @RequestParam(name = "shared", required = false) Boolean shared, @@ -45,7 +51,7 @@ public class ExperimentAPI { @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size ) { - Map experiments = experimentService.getExperiments( + Map experiments = experimentService.getExperiments(authentication, name, algorithm, shared, @@ -55,16 +61,16 @@ public class ExperimentAPI { size, orderBy, descending, - "(GET) /experiments"); + new Logger(activeUserService.getActiveUser().getUsername(),"(GET) /experiments")); return new ResponseEntity(experiments, HttpStatus.OK); } @ApiOperation(value = "Get an experiment", response = ExperimentDTO.class) @RequestMapping(value = "/{uuid}", method = RequestMethod.GET) - public ResponseEntity<String> getExperiment( + public ResponseEntity<String> getExperiment(Authentication authentication, @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - ExperimentDTO experimentDTO = experimentService.getExperiment(uuid, "(GET) /experiments/{uuid}"); + ExperimentDTO experimentDTO = experimentService.getExperiment(authentication, uuid, new Logger(activeUserService.getActiveUser().getUsername(),"(GET) /experiments/{uuid}")); return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); } @@ -72,7 +78,7 @@ public class ExperimentAPI { @ApiOperation(value = "Create an experiment", response = ExperimentDTO.class) @RequestMapping(method = RequestMethod.POST) public ResponseEntity<String> createExperiment(Authentication authentication, @RequestBody ExperimentDTO experimentDTO) { - experimentDTO = experimentService.createExperiment(authentication, experimentDTO, "(POST) /experiments"); + experimentDTO = experimentService.createExperiment(authentication, experimentDTO, new Logger(activeUserService.getActiveUser().getUsername(),"(POST) /experiments")); return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.CREATED); } @@ -80,7 +86,7 @@ public class ExperimentAPI { @ApiOperation(value = "Update an experiment", response = ExperimentDTO.class) @RequestMapping(value = "/{uuid}", method = RequestMethod.PATCH) public ResponseEntity<String> updateExperiment(@RequestBody ExperimentDTO experimentDTO, @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - experimentDTO = experimentService.updateExperiment(uuid, experimentDTO, "(PATCH) /experiments/{uuid}"); + experimentDTO = experimentService.updateExperiment(uuid, experimentDTO, new Logger(activeUserService.getActiveUser().getUsername(),"(PATCH) /experiments/{uuid}")); return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); } @@ -89,7 +95,7 @@ public class ExperimentAPI { @RequestMapping(value = "/{uuid}", method = RequestMethod.DELETE) public ResponseEntity<String> deleteExperiment( @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - experimentService.deleteExperiment(uuid, "(DELETE) /experiments/{uuid}"); + experimentService.deleteExperiment(uuid, new Logger(activeUserService.getActiveUser().getUsername(), "(DELETE) /experiments/{uuid}")); return new ResponseEntity<>(HttpStatus.OK); } @@ -97,8 +103,7 @@ public class ExperimentAPI { @ApiOperation(value = "Create a transient experiment", response = ExperimentDTO.class) @RequestMapping(value = "/transient", method = RequestMethod.POST) public ResponseEntity<String> createTransientExperiment(Authentication authentication, @RequestBody ExperimentDTO experimentDTO) { - experimentDTO = experimentService.createTransientExperiment(authentication, experimentDTO, "(POST) /experiments/transient"); - + experimentDTO = experimentService.createTransientExperiment(authentication, experimentDTO, new Logger(activeUserService.getActiveUser().getUsername(), "(POST) /experiments/transient")); return new ResponseEntity<>(JsonConverters.convertObjectToJsonString(experimentDTO), HttpStatus.OK); } } \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/controllers/PathologiesAPI.java b/src/main/java/eu/hbp/mip/controllers/PathologiesAPI.java index f79052ff5..175ad2977 100644 --- a/src/main/java/eu/hbp/mip/controllers/PathologiesAPI.java +++ b/src/main/java/eu/hbp/mip/controllers/PathologiesAPI.java @@ -8,9 +8,8 @@ import eu.hbp.mip.utils.ClaimUtils; import eu.hbp.mip.utils.CustomResourceLoader; import eu.hbp.mip.utils.Exceptions.BadRequestException; import eu.hbp.mip.utils.InputStreamConverter; -import eu.hbp.mip.utils.Logging; +import eu.hbp.mip.utils.Logger; import io.swagger.annotations.Api; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; import org.springframework.http.ResponseEntity; @@ -31,8 +30,6 @@ public class PathologiesAPI { private static final Gson gson = new Gson(); - @Autowired - private ActiveUserService activeUserService; // Enable HBP collab authentication (1) or disable it (0). Default is 1 @Value("#{'${authentication.enabled}'}") @@ -41,14 +38,19 @@ public class PathologiesAPI { @Value("#{'${files.pathologies_json}'}") private String pathologiesFilePath; - @Autowired - private CustomResourceLoader resourceLoader; + private final ActiveUserService activeUserService; + + private final CustomResourceLoader resourceLoader; + + public PathologiesAPI(ActiveUserService activeUserService, CustomResourceLoader resourceLoader) { + this.activeUserService = activeUserService; + this.resourceLoader = resourceLoader; + } @RequestMapping(name = "/pathologies", method = RequestMethod.GET) public ResponseEntity<String> getPathologies(Authentication authentication) { - String endpoint = "(GET) /pathologies"; - String username = activeUserService.getActiveUser().getUsername(); - Logging.LogUserAction(username, endpoint, "Loading pathologies ..."); + Logger logger = new Logger(activeUserService.getActiveUser().getUsername(), "(GET) /pathologies"); + logger.LogUserAction("Loading pathologies ..."); // Load pathologies from file Resource resource = resourceLoader.getResource(pathologiesFilePath); @@ -57,18 +59,18 @@ public class PathologiesAPI { allPathologies = gson.fromJson(InputStreamConverter.convertInputStreamToString(resource.getInputStream()), new TypeToken<List<PathologyDTO>>() { }.getType()); } catch (IOException e) { - Logging.LogUserAction(username, endpoint, "Unable to load pathologies"); + logger.LogUserAction("Unable to load pathologies"); throw new BadRequestException("The pathologies could not be loaded."); } // If authentication is disabled return everything if (!authenticationIsEnabled) { - Logging.LogUserAction(username, endpoint, "Successfully loaded " + allPathologies.size() + " pathologies"); + logger.LogUserAction("Successfully loaded " + allPathologies.size() + " pathologies"); return ResponseEntity.ok().body(gson.toJson(allPathologies)); } - Logging.LogUserAction(username, endpoint, "Successfully loaded all authorized pathologies"); + logger.LogUserAction("Successfully loaded all authorized pathologies"); return ResponseEntity.ok().body(ClaimUtils.getAuthorizedPathologies( - username, authentication.getAuthorities(), allPathologies)); + activeUserService.getActiveUser().getUsername(), logger, authentication.getAuthorities(), allPathologies)); } } diff --git a/src/main/java/eu/hbp/mip/services/ActiveUserService.java b/src/main/java/eu/hbp/mip/services/ActiveUserService.java index 61bad973c..f1fe049c2 100644 --- a/src/main/java/eu/hbp/mip/services/ActiveUserService.java +++ b/src/main/java/eu/hbp/mip/services/ActiveUserService.java @@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; @@ -19,12 +20,15 @@ import javax.inject.Named; public class ActiveUserService { @Value("#{'${authentication.enabled}'}") - private boolean authentication; + private boolean authenticationIsEnabled; private UserDAO user; - @Autowired - private UserRepository userRepository; + private final UserRepository userRepository; + + public ActiveUserService(UserRepository userRepository) { + this.userRepository = userRepository; + } /** * Fetches the details of the active user. @@ -39,13 +43,13 @@ public class ActiveUserService { return user; // If Authentication is OFF, create anonymous user with accepted NDA - if (!authentication) { + if (!authenticationIsEnabled) { user = new UserDAO("anonymous", "anonymous", "anonymous@anonymous.com"); user.setAgreeNDA(true); userRepository.save(user); return user; } - + //TODO: // If authentication is ON get user info from Token KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index 56861903c..e12c0b858 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -23,7 +23,7 @@ import eu.hbp.mip.utils.ClaimUtils; import eu.hbp.mip.utils.Exceptions.*; import eu.hbp.mip.utils.HTTPUtil; import eu.hbp.mip.utils.JsonConverters; -import eu.hbp.mip.utils.Logging; +import eu.hbp.mip.utils.Logger; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import org.springframework.beans.factory.annotation.Value; @@ -79,34 +79,36 @@ public class ExperimentService { * @param size is the size of each page * @param orderBy is the column that is required to ordered by * @param descending is a boolean to determine if the experiments will be order by descending or ascending - * @param endpoint is the endpoint that called the function + * @param logger contains username and the endpoint. * @return a list of mapped experiments */ - public Map getExperiments(String name, String algorithm, Boolean shared, Boolean viewed, boolean includeShared, int page, int size, String orderBy, Boolean descending, String endpoint) { + public Map getExperiments(Authentication authentication, String name, String algorithm, Boolean shared, Boolean viewed, boolean includeShared, int page, int size, String orderBy, Boolean descending, Logger logger) { + UserDAO user = activeUserService.getActiveUser(); - Logging.LogUserAction(user.getUsername(), endpoint, "Listing my experiments."); + logger.LogUserAction("Listing my experiments."); 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)); - + Specification<ExperimentDAO> spec; + if(authenticationIsEnabled && ClaimUtils.validateAccessRightsOnExperiments(user.getUsername(), authentication.getAuthorities(), logger)) + { + 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 { + 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); List<ExperimentDAO> experimentDAOs = pageExperiments.getContent(); @@ -130,24 +132,27 @@ public class ExperimentService { * The getExperiment will retrieve the experiment from database according to the input uuid * * @param uuid is the id of the experiment to be retrieved - * @param endpoint is the endpoint that called the function + * @param logger contains username and the endpoint. * @return the experiment information that was retrieved from the database */ - public ExperimentDTO getExperiment(String uuid, String endpoint) { + public ExperimentDTO getExperiment(Authentication authentication, String uuid, Logger logger) { ExperimentDAO experimentDAO; UserDAO user = activeUserService.getActiveUser(); - Logging.LogUserAction(user.getUsername(), endpoint, "Loading Experiment with uuid : " + uuid); + logger.LogUserAction("Loading Experiment with uuid : " + uuid); - experimentDAO = loadExperiment(uuid, endpoint); - //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."); + experimentDAO = loadExperiment(uuid, logger); + if ( + !experimentDAO.isShared() + && !experimentDAO.getCreatedBy().getUsername().equals(user.getUsername()) + && ClaimUtils.validateAccessRightsOnExperiments(user.getUsername(), authentication.getAuthorities(), logger) + ) { + logger.LogUserAction("Accessing Experiment is unauthorized."); throw new UnauthorizedException("You don't have access to the experiment."); } ExperimentDTO experimentDTO = experimentDAO.convertToDTO(true); - Logging.LogUserAction(user.getUsername(), endpoint, "Experiment was Loaded with uuid : " + uuid + "."); + logger.LogUserAction("Experiment was Loaded with uuid : " + uuid + "."); return experimentDTO; } @@ -157,37 +162,37 @@ public class ExperimentService { * * @param authentication is the role of the user * @param experimentDTO is the experiment information - * @param endpoint is the endpoint that called the function + * @param logger contains username and the endpoint. * @return the experiment information which was created */ - public ExperimentDTO createExperiment(Authentication authentication, ExperimentDTO experimentDTO, String endpoint) { + public ExperimentDTO createExperiment(Authentication authentication, ExperimentDTO experimentDTO, Logger logger) { UserDAO user = activeUserService.getActiveUser(); //Checking if check (POST) /experiments has proper input. - checkPostExperimentProperInput(experimentDTO, endpoint); + checkPostExperimentProperInput(experimentDTO, logger); // Get the type and name of algorithm String algorithmType = experimentDTO.getAlgorithm().getType(); if(algorithmType == null){ - Logging.LogUserAction(user.getUsername(), endpoint, "Please provide algorithm type."); + logger.LogUserAction("Please provide algorithm type."); throw new BadRequestException("Please provide algorithm type."); } - algorithmParametersLogging(experimentDTO, endpoint); + algorithmParametersLogging(experimentDTO, logger); if (authenticationIsEnabled) { - String experimentDatasets = getDatasetFromExperimentParameters(experimentDTO, endpoint); - ClaimUtils.validateAccessRightsOnDatasets(user.getUsername(), authentication.getAuthorities(), experimentDatasets, endpoint); + String experimentDatasets = getDatasetFromExperimentParameters(experimentDTO, logger); + ClaimUtils.validateAccessRightsOnDatasets(user.getUsername(), authentication.getAuthorities(), experimentDatasets, logger); } // Run with the appropriate engine if (algorithmType.equals("workflow")) { - Logging.LogUserAction(user.getUsername(), endpoint, "Algorithm runs on Galaxy."); - return runGalaxyWorkflow(experimentDTO, endpoint); + logger.LogUserAction("Algorithm runs on Galaxy."); + return runGalaxyWorkflow(experimentDTO, logger); } else { - Logging.LogUserAction(user.getUsername(), endpoint, "Algorithm runs on Exareme."); - return createExaremeExperiment(experimentDTO, endpoint); + logger.LogUserAction("Algorithm runs on Exareme."); + return createExaremeExperiment(experimentDTO, logger); } } @@ -196,14 +201,14 @@ public class ExperimentService { * * @param authentication is the role of the user * @param experimentDTO is the experiment information - * @param endpoint is the endpoint that called the function + * @param logger contains username and the endpoint. * @return the experiment information which was created */ - public ExperimentDTO createTransientExperiment(Authentication authentication, ExperimentDTO experimentDTO, String endpoint) { + public ExperimentDTO createTransientExperiment(Authentication authentication, ExperimentDTO experimentDTO, Logger logger) { UserDAO user = activeUserService.getActiveUser(); //Checking if check (POST) /experiments has proper input. - checkPostExperimentProperInput(experimentDTO, endpoint); + checkPostExperimentProperInput(experimentDTO, logger); // Get the parameters List<AlgorithmDTO.AlgorithmParamDTO> algorithmParameters @@ -212,24 +217,23 @@ public class ExperimentService { // Get the type and name of algorithm String algorithmName = experimentDTO.getAlgorithm().getName(); - algorithmParametersLogging(experimentDTO, endpoint); + algorithmParametersLogging(experimentDTO, logger); if (authenticationIsEnabled) { - String experimentDatasets = getDatasetFromExperimentParameters(experimentDTO, endpoint); - ClaimUtils.validateAccessRightsOnDatasets(user.getUsername(), authentication.getAuthorities(), experimentDatasets, endpoint); + String experimentDatasets = getDatasetFromExperimentParameters(experimentDTO, logger); + ClaimUtils.validateAccessRightsOnDatasets(user.getUsername(), authentication.getAuthorities(), experimentDatasets, logger); } String body = gson.toJson(algorithmParameters); String url = queryExaremeUrl + "/" + algorithmName; - Logging.LogUserAction(user.getUsername(), endpoint, "url: " + url + ", body: " + body); + logger.LogUserAction("url: " + url + ", body: " + body); - Logging.LogUserAction(user.getUsername(), endpoint, - "Completed, returning: " + experimentDTO.toString()); + logger.LogUserAction("Completed, returning: " + experimentDTO.toString()); // Results are stored in the experiment object ExaremeResult exaremeResult = runExaremeExperiment(url, body, experimentDTO); - Logging.LogUserAction(user.getUsername(), endpoint, "Experiment with uuid: " + experimentDTO.getUuid() + "gave response code: " + exaremeResult.getCode() + " and result: " + exaremeResult.getResults()); + logger.LogUserAction("Experiment with uuid: " + experimentDTO.getUuid() + "gave response code: " + exaremeResult.getCode() + " and result: " + exaremeResult.getResults()); experimentDTO.setResult((exaremeResult.getCode() >= 400) ? null : exaremeResult.getResults()); experimentDTO.setStatus((exaremeResult.getCode() >= 400) ? ExperimentDAO.Status.error : ExperimentDAO.Status.success); @@ -242,18 +246,17 @@ public class ExperimentService { * * @param uuid is the id of the experiment to be updated * @param experimentDTO is the experiment information to be updated - * @param endpoint is the endpoint that called the function - * @return the updated experiment information + * @param logger contains username and the endpoint. */ - public ExperimentDTO updateExperiment(String uuid, ExperimentDTO experimentDTO, String endpoint) { + public ExperimentDTO updateExperiment(String uuid, ExperimentDTO experimentDTO, Logger logger) { ExperimentDAO experimentDAO; UserDAO user = activeUserService.getActiveUser(); - Logging.LogUserAction(user.getUsername(), endpoint, "Updating experiment with uuid : " + uuid + "."); + logger.LogUserAction("Updating experiment with uuid : " + uuid + "."); - experimentDAO = loadExperiment(uuid, endpoint); + experimentDAO = loadExperiment(uuid, logger); //Verify (PATCH) /experiments non editable fields. - verifyPatchExperimentNonEditableFields(uuid, experimentDTO, experimentDAO, endpoint); + verifyPatchExperimentNonEditableFields(experimentDTO, logger); if (!experimentDAO.getCreatedBy().getUsername().equals(user.getUsername())) throw new UnauthorizedException("You don't have access to the experiment."); @@ -272,11 +275,11 @@ public class ExperimentService { try { experimentRepository.save(experimentDAO); } catch (Exception e) { - Logging.LogUserAction(user.getUsername(), endpoint, "Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); + logger.LogUserAction("Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); throw new InternalServerError(e.getMessage()); } - Logging.LogUserAction(user.getUsername(), endpoint, "Updated experiment with uuid : " + uuid + "."); + logger.LogUserAction("Updated experiment with uuid : " + uuid + "."); experimentDTO = experimentDAO.convertToDTO(true); return experimentDTO; @@ -286,14 +289,14 @@ public class ExperimentService { * The deleteExperiment will delete an experiment from the database * * @param uuid is the id of the experiment to be deleted - * @param endpoint is the endpoint that called the function + * @param logger contains username and the endpoint. */ - public void deleteExperiment(String uuid, String endpoint) { + public void deleteExperiment(String uuid, Logger logger) { ExperimentDAO experimentDAO; UserDAO user = activeUserService.getActiveUser(); - Logging.LogUserAction(user.getUsername(), endpoint, "Deleting experiment with uuid : " + uuid + "."); + logger.LogUserAction("Deleting experiment with uuid : " + uuid + "."); - experimentDAO = loadExperiment(uuid, endpoint); + experimentDAO = loadExperiment(uuid, logger); if (!experimentDAO.getCreatedBy().getUsername().equals(user.getUsername())) throw new UnauthorizedException("You don't have access to the experiment."); @@ -301,16 +304,16 @@ public class ExperimentService { try { experimentRepository.delete(experimentDAO); } catch (Exception e) { - Logging.LogUserAction(user.getUsername(), endpoint, "Attempted to delete an experiment to database but an error ocurred : " + e.getMessage() + "."); + logger.LogUserAction("Attempted to delete an experiment to database but an error ocurred : " + e.getMessage() + "."); throw new InternalServerError(e.getMessage()); } - Logging.LogUserAction(user.getUsername(), endpoint, "Deleted experiment with uuid : " + uuid + "."); + logger.LogUserAction("Deleted experiment with uuid : " + uuid + "."); } // /* ------------------------------- PRIVATE METHODS ----------------------------------------------------*/ - private void checkPostExperimentProperInput(ExperimentDTO experimentDTO, String endpoint) { + private void checkPostExperimentProperInput(ExperimentDTO experimentDTO, Logger logger) { boolean properInput = experimentDTO.getShared() == null @@ -322,50 +325,39 @@ public class ExperimentService { && experimentDTO.getUuid() == null; if (!properInput) { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "Invalid input."); + logger.LogUserAction( "Invalid input."); throw new BadRequestException("Please provide proper input."); } } - private void verifyPatchExperimentNonEditableFields(String uuid, ExperimentDTO experimentDTO, ExperimentDAO experimentDAO, String endpoint) { + private void verifyPatchExperimentNonEditableFields(ExperimentDTO experimentDTO, Logger logger) { if (experimentDTO.getUuid() != null ) { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "Uuid is not editable."); + logger.LogUserAction( "Uuid is not editable."); throw new BadRequestException("Uuid is not editable."); } if (experimentDTO.getAlgorithm() != null ) { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "Algorithm is not editable."); + logger.LogUserAction( "Algorithm is not editable."); throw new BadRequestException("Algorithm is not editable."); } if (experimentDTO.getCreated() != null) { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "CreatedBy is not editable."); - throw new BadRequestException("CreatedBy is not editable."); - } - - if (experimentDTO.getAlgorithm() != null) { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "AlgorithmDetails is not editable."); - throw new BadRequestException("AlgorithmDetails is not editable."); - } - - if (experimentDTO.getCreated() != null) { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "Created is not editable."); + logger.LogUserAction( "Created is not editable."); throw new BadRequestException("Created is not editable."); } if (experimentDTO.getResult() != null) { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "Status is not editable."); + logger.LogUserAction( "Status is not editable."); throw new BadRequestException("Status is not editable."); } if (experimentDTO.getStatus() != null) { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "Status is not editable."); + logger.LogUserAction( "Status is not editable."); throw new BadRequestException("Status is not editable."); } } - private void algorithmParametersLogging(ExperimentDTO experimentDTO, String endpoint) { - UserDAO user = activeUserService.getActiveUser(); + private void algorithmParametersLogging(ExperimentDTO experimentDTO, Logger logger) { String algorithmName = experimentDTO.getAlgorithm().getName(); StringBuilder parametersLogMessage = new StringBuilder(", Parameters:\n"); experimentDTO.getAlgorithm().getParameters().forEach( @@ -375,17 +367,17 @@ public class ExperimentService { .append(" -> ") .append(params.getValue()) .append("\n")); - Logging.LogUserAction(user.getUsername(), endpoint, "Executing " + algorithmName + parametersLogMessage); + logger.LogUserAction("Executing " + algorithmName + parametersLogMessage); } /** * The getDatasetFromExperimentParameters will retrieve the dataset from the experiment parameters * * @param experimentDTO is the experiment information - * @param endpoint is the endpoint that called the function + * @param logger contains username and the endpoint. * @return the dataset from the experiment */ - private String getDatasetFromExperimentParameters(ExperimentDTO experimentDTO, String endpoint) { + private String getDatasetFromExperimentParameters(ExperimentDTO experimentDTO, Logger logger) { String experimentDatasets = null; for (AlgorithmDTO.AlgorithmParamDTO parameter : experimentDTO.getAlgorithm().getParameters()) { @@ -396,8 +388,7 @@ public class ExperimentService { } if (experimentDatasets == null || experimentDatasets.equals("")) { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, - "A dataset should be specified to run an algorithm."); + logger.LogUserAction("A dataset should be specified to run an algorithm."); throw new BadRequestException("Please provide at least one dataset to run the algorithm."); } return experimentDatasets; @@ -409,20 +400,20 @@ public class ExperimentService { * @param uuid is the id of the experiment to be retrieved * @return the experiment information that was retrieved from database */ - private ExperimentDAO loadExperiment(String uuid, String endpoint) { + private ExperimentDAO loadExperiment(String uuid, Logger logger) { UUID experimentUuid; ExperimentDAO experimentDAO; try { experimentUuid = UUID.fromString(uuid); } catch (Exception e) { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, e.getMessage()); + logger.LogUserAction( e.getMessage()); throw new BadRequestException(e.getMessage()); } experimentDAO = experimentRepository.findByUuid(experimentUuid); if (experimentDAO == null) { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "Experiment with uuid : " + uuid + "was not found."); + logger.LogUserAction( "Experiment with uuid : " + uuid + "was not found."); throw new ExperimentNotFoundException("Experiment with uuid : " + uuid + " was not found."); } @@ -437,7 +428,7 @@ public class ExperimentService { * @Note In the database there will be stored Algorithm Details that is the whole information about the algorithm * and an Algorithm column that is required for the filtering with algorithm name in the GET /experiments. */ - private ExperimentDAO createExperimentInTheDatabase(ExperimentDTO experimentDTO, String endpoint) { + private ExperimentDAO createExperimentInTheDatabase(ExperimentDTO experimentDTO, Logger logger) { UserDAO user = activeUserService.getActiveUser(); ExperimentDAO experimentDAO = new ExperimentDAO(); @@ -451,42 +442,41 @@ public class ExperimentService { try { experimentRepository.save(experimentDAO); } catch (Exception e) { - Logging.LogUserAction(user.getUsername(), endpoint, "Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); + logger.LogUserAction("Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); throw new InternalServerError(e.getMessage()); } - Logging.LogUserAction(user.getUsername(), endpoint, " id : " + experimentDAO.getUuid()); - Logging.LogUserAction(user.getUsername(), endpoint, " algorithms : " + experimentDAO.getAlgorithmDetails()); - Logging.LogUserAction(user.getUsername(), endpoint, " name : " + experimentDAO.getName()); + logger.LogUserAction(" id : " + experimentDAO.getUuid()); + logger.LogUserAction(" algorithms : " + experimentDAO.getAlgorithmDetails()); + logger.LogUserAction(" name : " + experimentDAO.getName()); return experimentDAO; } - private void saveExperiment(ExperimentDAO experimentDAO, String endpoint) { - UserDAO user = activeUserService.getActiveUser(); + private void saveExperiment(ExperimentDAO experimentDAO, Logger logger) { - Logging.LogUserAction(user.getUsername(), endpoint, " id : " + experimentDAO.getUuid()); - Logging.LogUserAction(user.getUsername(), endpoint, " algorithms : " + experimentDAO.getAlgorithmDetails()); - Logging.LogUserAction(user.getUsername(), endpoint, " name : " + experimentDAO.getName()); - Logging.LogUserAction(user.getUsername(), endpoint, " historyId : " + experimentDAO.getWorkflowHistoryId()); - Logging.LogUserAction(user.getUsername(), endpoint, " status : " + experimentDAO.getStatus()); + logger.LogUserAction(" id : " + experimentDAO.getUuid()); + logger.LogUserAction(" algorithms : " + experimentDAO.getAlgorithmDetails()); + logger.LogUserAction(" name : " + experimentDAO.getName()); + logger.LogUserAction(" historyId : " + experimentDAO.getWorkflowHistoryId()); + logger.LogUserAction(" status : " + experimentDAO.getStatus()); try { experimentRepository.save(experimentDAO); } catch (Exception e) { - Logging.LogUserAction(user.getUsername(), endpoint, "Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); + logger.LogUserAction("Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); throw new InternalServerError(e.getMessage()); } - Logging.LogUserAction(user.getUsername(), endpoint, "Saved experiment"); + logger.LogUserAction("Saved experiment"); } - private void finishExperiment(ExperimentDAO experimentDAO, String endpoint) { + private void finishExperiment(ExperimentDAO experimentDAO, Logger logger) { experimentDAO.setFinished(new Date()); try { experimentRepository.save(experimentDAO); } catch (Exception e) { - Logging.LogUserAction(activeUserService.getActiveUser().getUsername(), endpoint, "Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); + logger.LogUserAction( "Attempted to save changes to database but an error ocurred : " + e.getMessage() + "."); throw new InternalServerError(e.getMessage()); } } @@ -497,16 +487,15 @@ public class ExperimentService { * The createExaremeExperiment will POST the algorithm to the exareme client * * @param experimentDTO is the request with the experiment information - * @param endpoint is the endpoint that called the function + * @param logger contains username and the endpoint. * @return the experiment information that was retrieved from exareme */ - public ExperimentDTO createExaremeExperiment(ExperimentDTO experimentDTO, String endpoint) { - UserDAO user = activeUserService.getActiveUser(); + public ExperimentDTO createExaremeExperiment(ExperimentDTO experimentDTO, Logger logger) { - Logging.LogUserAction(user.getUsername(), endpoint, "Running the algorithm..."); + logger.LogUserAction("Running the algorithm..."); - ExperimentDAO experimentDAO = createExperimentInTheDatabase(experimentDTO, endpoint); - Logging.LogUserAction(user.getUsername(), endpoint, "Created experiment with uuid :" + experimentDAO.getUuid()); + ExperimentDAO experimentDAO = createExperimentInTheDatabase(experimentDTO, logger); + logger.LogUserAction("Created experiment with uuid :" + experimentDAO.getUuid()); // Run the 1st algorithm from the list String algorithmName = experimentDTO.getAlgorithm().getName(); @@ -517,35 +506,33 @@ public class ExperimentService { String body = gson.toJson(algorithmParameters); String url = queryExaremeUrl + "/" + algorithmName; - Logging.LogUserAction(user.getUsername(), endpoint, "url: " + url + ", body: " + body); + logger.LogUserAction("url: " + url + ", body: " + body); - Logging.LogUserAction(user.getUsername(), endpoint, - "Completed, returning: " + experimentDTO.toString()); + logger.LogUserAction("Completed, returning: " + experimentDTO.toString()); - Logging.LogUserAction(user.getUsername(), endpoint, - "Starting exareme execution thread"); + logger.LogUserAction("Starting exareme execution thread"); ExperimentDTO finalExperimentDTO = experimentDTO; new Thread(() -> { // ATTENTION: Inside the Thread only LogExperimentAction should be used, not LogUserAction! - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Thread named :" + Thread.currentThread().getName() + " with id :" + Thread.currentThread().getId() + " started!"); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Thread named :" + Thread.currentThread().getName() + " with id :" + Thread.currentThread().getId() + " started!"); try { // Results are stored in the experiment object ExaremeResult exaremeResult = runExaremeExperiment(url, body, finalExperimentDTO); - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Experiment with uuid: " + experimentDAO.getUuid() + "gave response code: " + exaremeResult.getCode() + " and result: " + exaremeResult.getResults()); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Experiment with uuid: " + experimentDAO.getUuid() + "gave response code: " + exaremeResult.getCode() + " and result: " + exaremeResult.getResults()); experimentDAO.setResult((exaremeResult.getCode() >= 400) ? null : JsonConverters.convertObjectToJsonString(exaremeResult.getResults())); experimentDAO.setStatus((exaremeResult.getCode() >= 400) ? ExperimentDAO.Status.error : ExperimentDAO.Status.success); } catch (Exception e) { - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "There was an exception: " + e.getMessage()); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "There was an exception: " + e.getMessage()); experimentDAO.setStatus(ExperimentDAO.Status.error); } - finishExperiment(experimentDAO, endpoint); - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Finished the experiment: " + experimentDAO.toString()); + finishExperiment(experimentDAO, logger); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Finished the experiment: " + experimentDAO.toString()); }).start(); experimentDTO = experimentDAO.convertToDTO(true); return experimentDTO; @@ -568,7 +555,7 @@ public class ExperimentService { } catch (Exception e) { throw new InternalServerError("Error occurred : " + e.getMessage()); } - Logging.LogExperimentAction(experimentDTO.getName(), experimentDTO.getUuid(), "Algorithm finished with code: " + code); + Logger.LogExperimentAction(experimentDTO.getName(), experimentDTO.getUuid(), "Algorithm finished with code: " + code); // Results are stored in the experiment object ExperimentDTO experimentDTOWithOnlyResult = JsonConverters.convertJsonStringToObject(String.valueOf(results), ExperimentDTO.class); @@ -586,12 +573,11 @@ public class ExperimentService { * @param experimentDTO is the request with the experiment information * @return the response to be returned */ - public ExperimentDTO runGalaxyWorkflow(ExperimentDTO experimentDTO, String endpoint) { - UserDAO user = activeUserService.getActiveUser(); - Logging.LogUserAction(user.getUsername(), endpoint, "Running a workflow..."); + public ExperimentDTO runGalaxyWorkflow(ExperimentDTO experimentDTO, Logger logger) { + logger.LogUserAction("Running a workflow..."); - ExperimentDAO experimentDAO = createExperimentInTheDatabase(experimentDTO, endpoint); - Logging.LogUserAction(user.getUsername(), endpoint, "Created experiment with uuid :" + experimentDAO.getUuid()); + ExperimentDAO experimentDAO = createExperimentInTheDatabase(experimentDTO, logger); + logger.LogUserAction("Created experiment with uuid :" + experimentDAO.getUuid()); // Run the 1st algorithm from the list @@ -620,8 +606,7 @@ public class ExperimentService { } } if (workflow == null) { - Logging.LogUserAction(user.getUsername(), endpoint, - "Could not find algorithm code: " + workflowId); + logger.LogUserAction("Could not find algorithm code: " + workflowId); throw new BadRequestException("Could not find galaxy algorithm."); } final WorkflowDetails workflowDetails = workflowsClient.showWorkflow(workflow.getId()); @@ -638,7 +623,7 @@ public class ExperimentService { // Create the request client RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); - Logging.LogUserAction(user.getUsername(), endpoint, "Running Galaxy workflow with id: " + workflow.getId()); + logger.LogUserAction("Running Galaxy workflow with id: " + workflow.getId()); // Call Galaxy to run the workflow Call<PostWorkflowToGalaxyDtoResponse> call = service.postWorkflowToGalaxy(workflow.getId(), galaxyApiKey, requestBodyJson); @@ -647,7 +632,7 @@ public class ExperimentService { if (response.code() == 200) { // Call succeeded String responseBody = gson.toJson(response.body()); - Logging.LogUserAction(user.getUsername(), endpoint, "Response: " + responseBody); + logger.LogUserAction("Response: " + responseBody); String historyId = (String) new JSONObject(responseBody).get("history_id"); experimentDAO.setWorkflowHistoryId(historyId); @@ -655,7 +640,7 @@ public class ExperimentService { } else { // Something unexpected happened String msgErr = gson.toJson(response.errorBody()); - Logging.LogUserAction(user.getUsername(), endpoint, "Error Response: " + msgErr); + logger.LogUserAction("Error Response: " + msgErr); // Values are read from streams. JSONObject jObjectError = new JSONObject(msgErr); @@ -665,15 +650,15 @@ public class ExperimentService { } } catch (Exception e) { - Logging.LogUserAction(user.getUsername(), endpoint, "An exception occurred: " + e.getMessage()); + logger.LogUserAction("An exception occurred: " + e.getMessage()); experimentDAO.setStatus(ExperimentDAO.Status.error); } - saveExperiment(experimentDAO, endpoint); + saveExperiment(experimentDAO, logger); // Start the process of fetching the status - updateWorkflowExperiment(experimentDAO, endpoint); + updateWorkflowExperiment(experimentDAO, logger); - Logging.LogUserAction(user.getUsername(), endpoint, "Run workflow completed!"); + logger.LogUserAction("Run workflow completed!"); experimentDTO = experimentDAO.convertToDTO(true); return experimentDTO; @@ -685,59 +670,56 @@ public class ExperimentService { * * @param experimentDAO will be used to fetch it's workflow status, it should have the workflowHistoryId initialized * and the result should not already be fetched - * @return nothing, just updates the experiment */ - public void updateWorkflowExperiment(ExperimentDAO experimentDAO, String endpoint) { - UserDAO user = activeUserService.getActiveUser(); + public void updateWorkflowExperiment(ExperimentDAO experimentDAO, Logger logger) { if (experimentDAO == null) { - Logging.LogUserAction(user.getUsername(), endpoint, "The experiment does not exist."); + logger.LogUserAction("The experiment does not exist."); return; } - Logging.LogUserAction(user.getUsername(), endpoint, - " Experiment id : " + experimentDAO.getUuid()); + logger.LogUserAction(" Experiment id : " + experimentDAO.getUuid()); if (experimentDAO.getWorkflowHistoryId() == null) { - Logging.LogUserAction(user.getUsername(), endpoint, "History Id does not exist."); + logger.LogUserAction("History Id does not exist."); return; } - Logging.LogUserAction(user.getUsername(), endpoint, "Starting Thread..."); + logger.LogUserAction("Starting Thread..."); new Thread(() -> { while (true) { // ATTENTION: Inside the Thread only LogExperimentAction should be used, not LogExperimentAction! - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Thread is running..."); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Thread is running..."); try { sleep(2000); } catch (InterruptedException e) { - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Sleep was disrupted: " + e.getMessage()); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Sleep was disrupted: " + e.getMessage()); } - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Fetching status for experiment Id: " + experimentDAO.getUuid()); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Fetching status for experiment Id: " + experimentDAO.getUuid()); String state = getWorkflowStatus(experimentDAO); - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "State is: " + state); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "State is: " + state); switch (state) { case "running": // Do nothing, when the experiment is created the status is set to running - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Workflow is still running."); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Workflow is still running."); break; case "completed": // Get only the job result that is visible List<GalaxyWorkflowResult> workflowJobsResults = getWorkflowResults(experimentDAO); - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Results are: " + workflowJobsResults.toString()); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Results are: " + workflowJobsResults.toString()); boolean resultFound = false; for (GalaxyWorkflowResult jobResult : workflowJobsResults) { if (jobResult.getVisible()) { - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Visible result are: " + jobResult.getId()); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Visible result are: " + jobResult.getId()); String result = getWorkflowResultBody(experimentDAO, jobResult.getId()); - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "ResultDTO: " + result); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "ResultDTO: " + result); if (result == null) { experimentDAO.setStatus(ExperimentDAO.Status.error); } else { @@ -749,26 +731,26 @@ public class ExperimentService { } if (!resultFound) { // If there is no visible result - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "No visible result"); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "No visible result"); experimentDAO.setStatus(ExperimentDAO.Status.error); } - finishExperiment(experimentDAO, endpoint); + finishExperiment(experimentDAO, logger); break; case "error": // Get the job result that failed workflowJobsResults = getWorkflowResults(experimentDAO); - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Error results are: " + workflowJobsResults.toString()); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Error results are: " + workflowJobsResults.toString()); boolean failedJobFound = false; for (GalaxyWorkflowResult jobResult : workflowJobsResults) { if (jobResult.getState().equals("error")) { - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Failed job is: " + jobResult.getId()); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Failed job is: " + jobResult.getId()); String result = getWorkflowJobError(jobResult.getId(), experimentDAO); - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Job result: " + result); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Job result: " + result); if (result == null) { experimentDAO.setStatus(ExperimentDAO.Status.error); } @@ -778,22 +760,22 @@ public class ExperimentService { } if (!failedJobFound) { // If there is no visible failed job - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "No failed result"); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "No failed result"); experimentDAO.setStatus(ExperimentDAO.Status.error); } - finishExperiment(experimentDAO, endpoint); + finishExperiment(experimentDAO, logger); break; default: // InternalError or unexpected result - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "An unexpected error occurred."); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "An unexpected error occurred."); experimentDAO.setStatus(ExperimentDAO.Status.error); - finishExperiment(experimentDAO, endpoint); + finishExperiment(experimentDAO, logger); break; } // If result exists return if (experimentDAO.getResult() != null) { - Logging.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "ResultDTO exists: " + experimentDAO.getResult()); + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "ResultDTO exists: " + experimentDAO.getResult()); return; } } @@ -814,7 +796,7 @@ public class ExperimentService { UUID experimentId = experimentDAO.getUuid(); // ATTENTION: This function is used from a Thread. Only LogExperimentAction should be used, not LogUserAction! - Logging.LogExperimentAction(experimentName, experimentId, " History Id : " + historyId); + Logger.LogExperimentAction(experimentName, experimentId, " History Id : " + historyId); // Create the request client RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); @@ -824,15 +806,15 @@ public class ExperimentService { try { Response<Object> response = call.execute(); if (response.code() >= 400) { - Logging.LogExperimentAction(experimentName, experimentId, " Response code: " + Logger.LogExperimentAction(experimentName, experimentId, " Response code: " + response.code() + "" + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); return "internalError"; } result = new Gson().toJson(response.body()); - Logging.LogExperimentAction(experimentName, experimentId, " ResultDTO: " + result); + Logger.LogExperimentAction(experimentName, experimentId, " ResultDTO: " + result); } catch (IOException e) { - Logging.LogExperimentAction(experimentName, experimentId, " An exception happened: " + e.getMessage()); + Logger.LogExperimentAction(experimentName, experimentId, " An exception happened: " + e.getMessage()); return "internalError"; } @@ -841,11 +823,11 @@ public class ExperimentService { JSONObject resultJson = new JSONObject(result); state = resultJson.getString("state"); } catch (JSONException e) { - Logging.LogExperimentAction(experimentName, experimentId, " An exception happened: " + e.getMessage()); + Logger.LogExperimentAction(experimentName, experimentId, " An exception happened: " + e.getMessage()); return "internalError"; } - Logging.LogExperimentAction(experimentName, experimentId, " Completed!"); + Logger.LogExperimentAction(experimentName, experimentId, " Completed!"); switch (state) { case "ok": return "completed"; @@ -870,7 +852,7 @@ public class ExperimentService { String historyId = experimentDAO.getWorkflowHistoryId(); String experimentName = experimentDAO.getName(); UUID experimentId = experimentDAO.getUuid(); - Logging.LogExperimentAction(experimentName, experimentId, " historyId : " + historyId); + Logger.LogExperimentAction(experimentName, experimentId, " historyId : " + historyId); RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); Call<List<GalaxyWorkflowResult>> call = service.getWorkflowResultsFromGalaxy(historyId, galaxyApiKey); @@ -879,19 +861,19 @@ public class ExperimentService { try { Response<List<GalaxyWorkflowResult>> response = call.execute(); if (response.code() >= 400) { - Logging.LogExperimentAction(experimentName, experimentId, " Response code: " + Logger.LogExperimentAction(experimentName, experimentId, " Response code: " + response.code() + "" + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); return null; } getGalaxyWorkflowResultList = response.body(); - Logging.LogExperimentAction(experimentName, experimentId, " ResultDTO: " + response.body()); + Logger.LogExperimentAction(experimentName, experimentId, " ResultDTO: " + response.body()); } catch (IOException e) { - Logging.LogExperimentAction(experimentName, experimentId, " An exception happened: " + e.getMessage()); + Logger.LogExperimentAction(experimentName, experimentId, " An exception happened: " + e.getMessage()); return null; } - Logging.LogExperimentAction(experimentName, experimentId, " Completed!"); + Logger.LogExperimentAction(experimentName, experimentId, " Completed!"); return getGalaxyWorkflowResultList; } @@ -907,7 +889,7 @@ public class ExperimentService { String experimentName = experimentDAO.getName(); UUID experimentId = experimentDAO.getUuid(); - Logging.LogExperimentAction(experimentName, experimentId, " historyId : " + historyId); + Logger.LogExperimentAction(experimentName, experimentId, " historyId : " + historyId); RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); Call<Object> call = @@ -917,20 +899,20 @@ public class ExperimentService { try { Response<Object> response = call.execute(); if (response.code() >= 400) { - Logging.LogExperimentAction(experimentName, experimentId, " Response code: " + Logger.LogExperimentAction(experimentName, experimentId, " Response code: " + response.code() + "" + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); return null; } resultJson = new Gson().toJson(response.body()); - Logging.LogExperimentAction(experimentName, experimentId, " ResultDTO: " + resultJson); + Logger.LogExperimentAction(experimentName, experimentId, " ResultDTO: " + resultJson); } catch (IOException e) { - Logging.LogExperimentAction(experimentName, experimentId, + Logger.LogExperimentAction(experimentName, experimentId, " An exception happened: " + e.getMessage()); return null; } - Logging.LogExperimentAction(experimentName, experimentId, " Completed!"); + Logger.LogExperimentAction(experimentName, experimentId, " Completed!"); return resultJson; } @@ -943,7 +925,7 @@ public class ExperimentService { String experimentName = experimentDAO.getName(); UUID experimentId = experimentDAO.getUuid(); - Logging.LogExperimentAction(experimentName, experimentId, " jobId : " + jobId); + Logger.LogExperimentAction(experimentName, experimentId, " jobId : " + jobId); RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); Call<Object> callError = service.getErrorMessageOfWorkflowFromGalaxy(jobId, galaxyApiKey); @@ -952,7 +934,7 @@ public class ExperimentService { try { Response<Object> response = callError.execute(); if (response.code() >= 400) { - Logging.LogExperimentAction(experimentName, experimentId, "Response code: " + Logger.LogExperimentAction(experimentName, experimentId, "Response code: " + response.code() + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); return null; } @@ -962,19 +944,19 @@ public class ExperimentService { JsonElement jsonElement = new JsonParser().parse(jsonString); JsonObject rootObject = jsonElement.getAsJsonObject(); fullError = rootObject.get("stderr").getAsString(); - Logging.LogExperimentAction(experimentName, experimentId, "Error: " + fullError); + Logger.LogExperimentAction(experimentName, experimentId, "Error: " + fullError); String[] arrOfStr = fullError.split("ValueError", 0); String specError = arrOfStr[arrOfStr.length - 1]; returnError = specError.substring(1); - Logging.LogExperimentAction(experimentName, experimentId, "Parsed Error: " + returnError); + Logger.LogExperimentAction(experimentName, experimentId, "Parsed Error: " + returnError); } catch (IOException e) { - Logging.LogExperimentAction(experimentName, experimentId, "Exception: " + e.getMessage()); + Logger.LogExperimentAction(experimentName, experimentId, "Exception: " + e.getMessage()); return null; } - Logging.LogExperimentAction(experimentName, experimentId, "Completed successfully!"); + Logger.LogExperimentAction(experimentName, experimentId, "Completed successfully!"); return returnError; } diff --git a/src/main/java/eu/hbp/mip/utils/ClaimUtils.java b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java index 5eb90a2d0..501cea1b0 100644 --- a/src/main/java/eu/hbp/mip/utils/ClaimUtils.java +++ b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java @@ -2,14 +2,10 @@ 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; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; +import java.util.*; public class ClaimUtils { @@ -17,67 +13,62 @@ public class ClaimUtils { private static final Gson gson = new Gson(); public static String allDatasetsAllowedClaim() { - return "role_dataset_all"; + return "role_research_dataset_all"; + } + + public static String allExperimentsAllowedClaim() { + return "role_research_experiment_all"; } public static String getDatasetClaim(String datasetCode) { - return "role_dataset_" + datasetCode.toLowerCase(); + return "role_research_dataset_" + datasetCode.toLowerCase(); } public static void validateAccessRightsOnDatasets(String username, Collection<? extends GrantedAuthority> authorities, - String experimentDatasets, String endpoint) { - - List<String> userClaims = Arrays.asList(authorities.toString().toLowerCase() - .replaceAll("[\\s+\\]\\[]", "").split(",")); - Logging.LogUserAction(username, endpoint, userClaims.toString()); + String experimentDatasets, Logger logger) { // Don't check for dataset claims if "super" claim exists allowing everything - if (!userClaims.contains(ClaimUtils.allDatasetsAllowedClaim())) { + if (!hasRoleAccess(username, authorities, ClaimUtils.allDatasetsAllowedClaim(), logger)) { for (String dataset : experimentDatasets.split(",")) { String datasetRole = ClaimUtils.getDatasetClaim(dataset); - if (!userClaims.contains(datasetRole.toLowerCase())) { - Logging.LogUserAction(username, endpoint, - "You are not allowed to use dataset: " + dataset); + if (!hasRoleAccess(username, authorities, datasetRole, logger)) { + logger.LogUserAction("You are not allowed to use dataset: " + dataset); throw new UnauthorizedException("You are not authorized to use these datasets."); } } - Logging.LogUserAction(username, endpoint, - "User is authorized to use the datasets: " + experimentDatasets); + logger.LogUserAction("User is authorized to use the datasets: " + experimentDatasets); } } - public static String getAuthorizedPathologies(String username, Collection<? extends GrantedAuthority> authorities, - List<PathologyDTO> allPathologies) { - // --- Providing only the allowed pathologies/datasets to the user --- - Logging.LogUserAction(username, - "(GET) /pathologies", "Filter out the unauthorised datasets."); + public static boolean validateAccessRightsOnExperiments(String username, Collection<? extends GrantedAuthority> authorities, Logger logger) { - List<String> userClaims = Arrays.asList(authorities.toString().toLowerCase() - .replaceAll("[\\s+\\]\\[]", "").split(",")); + // Check for experiment_all claims + return hasRoleAccess(username, authorities, ClaimUtils.allExperimentsAllowedClaim(), logger); + } - Logging.LogUserAction(username, - "(GET) /pathologies", "User Claims: " + userClaims); + public static String getAuthorizedPathologies(String username, Logger logger, Collection<? extends GrantedAuthority> authorities, + List<PathologyDTO> allPathologies) { + // --- Providing only the allowed pathologies/datasets to the user --- + logger.LogUserAction("Filter out the unauthorised datasets."); // If the "dataset_all" claim exists then return everything - if (userClaims.contains(ClaimUtils.allDatasetsAllowedClaim())) { + if (hasRoleAccess(username, authorities, ClaimUtils.allDatasetsAllowedClaim(), logger)) { return gson.toJson(allPathologies); } List<PathologyDTO> userPathologies = new ArrayList<>(); for (PathologyDTO curPathology : allPathologies) { - List<PathologyDTO.PathologyDatasetDTO> userPathologyDatasets = new ArrayList<PathologyDTO.PathologyDatasetDTO>(); + List<PathologyDTO.PathologyDatasetDTO> userPathologyDatasets = new ArrayList<>(); for (PathologyDTO.PathologyDatasetDTO dataset : curPathology.getDatasets()) { - if (userClaims.contains(ClaimUtils.getDatasetClaim(dataset.getCode()))) { - Logging.LogUserAction(username, "(GET) /pathologies", - "Added dataset: " + dataset.getCode()); + if (hasRoleAccess(username, authorities, ClaimUtils.getDatasetClaim(dataset.getCode()), logger)) { + logger.LogUserAction("Added dataset: " + dataset.getCode()); userPathologyDatasets.add(dataset); } } if (userPathologyDatasets.size() > 0) { - Logging.LogUserAction(username, "(GET) /pathologies", - "Added pathology '" + curPathology.getLabel() + logger.LogUserAction("Added pathology '" + curPathology.getLabel() + "' with datasets: '" + userPathologyDatasets + "'"); PathologyDTO userPathology = new PathologyDTO(); @@ -92,4 +83,12 @@ public class ClaimUtils { return gson.toJson(userPathologies); } + private static boolean hasRoleAccess(String username, Collection<? extends GrantedAuthority> authorities,String role, Logger logger) + { + List<String> userClaims = Arrays.asList(authorities.toString().toLowerCase() + .replaceAll("[\\s+\\]\\[]", "").split(",")); + + logger.LogUserAction("User Claims: " + userClaims); + return userClaims.contains(role.toLowerCase()); + } } diff --git a/src/main/java/eu/hbp/mip/utils/Logging.java b/src/main/java/eu/hbp/mip/utils/Logger.java similarity index 53% rename from src/main/java/eu/hbp/mip/utils/Logging.java rename to src/main/java/eu/hbp/mip/utils/Logger.java index 9b75de636..1e25d2c92 100644 --- a/src/main/java/eu/hbp/mip/utils/Logging.java +++ b/src/main/java/eu/hbp/mip/utils/Logger.java @@ -1,16 +1,23 @@ package eu.hbp.mip.utils; -import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.UUID; -public class Logging { - private static final Logger LOGGER = LoggerFactory.getLogger(Logging.class); +public class Logger { - public static void LogUserAction(String userName, String endpoint, String actionInfo) { - LOGGER.info(" User -> " + userName + " ," + private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(Logger.class); + private String username; + private String endpoint; + + public Logger(String username, String endpoint){ + this.username = username; + this.endpoint = endpoint; + } + + public void LogUserAction(String actionInfo) { + LOGGER.info(" User -> " + username + " ," + "Endpoint -> " + endpoint + " ," + "Info -> " + actionInfo); } -- GitLab From ba11d26d5004957f559276bb1ba514c1df468106 Mon Sep 17 00:00:00 2001 From: kostas <kostasfilippop@gmail.com> Date: Wed, 23 Dec 2020 04:08:23 -0800 Subject: [PATCH 46/52] Updated the migration so that is backwards compatible. --- .../eu/hbp/mip/models/DAOs/ExperimentDAO.java | 25 +++++++------ .../eu/hbp/mip/models/DTOs/ExperimentDTO.java | 6 +-- .../hbp/mip/services/ExperimentService.java | 34 ++++++++++++----- src/main/resources/application.yml | 2 +- .../migration/V7_0__NewDatabaseStructure.sql | 37 +++++++++++++++++-- 5 files changed, 76 insertions(+), 28 deletions(-) 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 f634c77a2..30d5ee123 100644 --- a/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java +++ b/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java @@ -43,6 +43,7 @@ public class ExperimentDAO { @Expose @Column(columnDefinition = "TEXT") + @Enumerated(EnumType.STRING) private Status status; @Expose @@ -55,11 +56,11 @@ public class ExperimentDAO { @Expose @Column(columnDefinition = "TEXT") - private String algorithmDetails; + private String algorithm; @Expose @Column(columnDefinition = "TEXT") - private String algorithm; + private String algorithmId; @Expose @Column(columnDefinition = "TIMESTAMP WITHOUT TIME ZONE") @@ -93,14 +94,14 @@ public class ExperimentDAO { public ExperimentDTO convertToDTO(boolean includeResult) { ExperimentDTO experimentDTO = new ExperimentDTO(); - experimentDTO.setAlgorithm(JsonConverters.convertJsonStringToObject(this.algorithmDetails, AlgorithmDTO.class)); + experimentDTO.setAlgorithm(JsonConverters.convertJsonStringToObject(this.algorithm, AlgorithmDTO.class)); experimentDTO.setCreated(this.created); experimentDTO.setUpdated(this.updated); experimentDTO.setFinished(this.finished); experimentDTO.setCreatedBy(this.createdBy.getUsername()); experimentDTO.setName(this.name); if(includeResult){ - experimentDTO.setResult(JsonConverters.convertJsonStringToObject(String.valueOf(this.result), new ArrayList<ExperimentDTO.ResultDTO>().getClass())); + experimentDTO.setResult(JsonConverters.convertJsonStringToObject(String.valueOf(this.result), new ArrayList<ExperimentDTO.ResultDTO>().getClass())); } experimentDTO.setStatus(this.status); experimentDTO.setShared(this.shared); @@ -109,14 +110,6 @@ public class ExperimentDAO { return experimentDTO; } - public String getAlgorithmDetails() { - return algorithmDetails; - } - - public void setAlgorithmDetails(String algorithmDetails) { - this.algorithmDetails = algorithmDetails; - } - public String getAlgorithm() { return algorithm; } @@ -125,6 +118,14 @@ public class ExperimentDAO { this.algorithm = algorithm; } + public String getAlgorithmId() { + return algorithmId; + } + + public void setAlgorithmId(String algorithmId) { + this.algorithmId = algorithmId; + } + public String getWorkflowHistoryId() { return workflowHistoryId; } 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 a1c6e916c..2840f669f 100644 --- a/src/main/java/eu/hbp/mip/models/DTOs/ExperimentDTO.java +++ b/src/main/java/eu/hbp/mip/models/DTOs/ExperimentDTO.java @@ -117,7 +117,7 @@ public class ExperimentDTO { public static class ResultDTO { private Object data; - private Object type; + private String type; public Object getData() { return this.data; @@ -127,11 +127,11 @@ public class ExperimentDTO { this.data = data; } - public Object getType() { + public String getType() { return type; } - public void setType(Object type) { + public void setType(String type) { this.type = type; } } diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index e12c0b858..869db8836 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -59,7 +59,7 @@ public class ExperimentService { private static final Gson gson = new Gson(); - private ActiveUserService activeUserService; + private final ActiveUserService activeUserService; private final ExperimentRepository experimentRepository; public ExperimentService(ActiveUserService activeUserService, ExperimentRepository experimentRepository) { @@ -90,7 +90,7 @@ public class ExperimentService { if (size > 50) throw new BadRequestException("Invalid size input, max size is 50."); Specification<ExperimentDAO> spec; - if(authenticationIsEnabled && ClaimUtils.validateAccessRightsOnExperiments(user.getUsername(), authentication.getAuthorities(), logger)) + if(!authenticationIsEnabled || ClaimUtils.validateAccessRightsOnExperiments(user.getUsername(), authentication.getAuthorities(), logger)) { spec = Specification .where(new ExperimentSpecifications.ExperimentWithName(name)) @@ -146,6 +146,7 @@ public class ExperimentService { if ( !experimentDAO.isShared() && !experimentDAO.getCreatedBy().getUsername().equals(user.getUsername()) + && authenticationIsEnabled && ClaimUtils.validateAccessRightsOnExperiments(user.getUsername(), authentication.getAuthorities(), logger) ) { logger.LogUserAction("Accessing Experiment is unauthorized."); @@ -346,9 +347,24 @@ public class ExperimentService { throw new BadRequestException("Created is not editable."); } + if (experimentDTO.getCreatedBy() != null) { + logger.LogUserAction( "CreatedBy is not editable."); + throw new BadRequestException("CreatedBy is not editable."); + } + + if (experimentDTO.getUpdated() != null) { + logger.LogUserAction( "Updated is not editable."); + throw new BadRequestException("Updated is not editable."); + } + + if (experimentDTO.getFinished() != null) { + logger.LogUserAction( "Finished is not editable."); + throw new BadRequestException("Finished is not editable."); + } + if (experimentDTO.getResult() != null) { - logger.LogUserAction( "Status is not editable."); - throw new BadRequestException("Status is not editable."); + logger.LogUserAction( "Result is not editable."); + throw new BadRequestException("Result is not editable."); } if (experimentDTO.getStatus() != null) { @@ -369,7 +385,7 @@ public class ExperimentService { .append("\n")); logger.LogUserAction("Executing " + algorithmName + parametersLogMessage); } - + /** * The getDatasetFromExperimentParameters will retrieve the dataset from the experiment parameters * @@ -434,8 +450,8 @@ public class ExperimentService { ExperimentDAO experimentDAO = new ExperimentDAO(); experimentDAO.setUuid(UUID.randomUUID()); experimentDAO.setCreatedBy(user); - experimentDAO.setAlgorithmDetails(JsonConverters.convertObjectToJsonString(experimentDTO.getAlgorithm())); - experimentDAO.setAlgorithm(experimentDTO.getAlgorithm().getName()); + experimentDAO.setAlgorithm(JsonConverters.convertObjectToJsonString(experimentDTO.getAlgorithm())); + experimentDAO.setAlgorithmId(experimentDTO.getAlgorithm().getName()); experimentDAO.setName(experimentDTO.getName()); experimentDAO.setStatus(ExperimentDAO.Status.pending); @@ -447,7 +463,7 @@ public class ExperimentService { } logger.LogUserAction(" id : " + experimentDAO.getUuid()); - logger.LogUserAction(" algorithms : " + experimentDAO.getAlgorithmDetails()); + logger.LogUserAction(" algorithm : " + experimentDAO.getAlgorithm()); logger.LogUserAction(" name : " + experimentDAO.getName()); return experimentDAO; } @@ -455,7 +471,7 @@ public class ExperimentService { private void saveExperiment(ExperimentDAO experimentDAO, Logger logger) { logger.LogUserAction(" id : " + experimentDAO.getUuid()); - logger.LogUserAction(" algorithms : " + experimentDAO.getAlgorithmDetails()); + logger.LogUserAction(" algorithm : " + experimentDAO.getAlgorithm()); logger.LogUserAction(" name : " + experimentDAO.getName()); logger.LogUserAction(" historyId : " + experimentDAO.getWorkflowHistoryId()); logger.LogUserAction(" status : " + experimentDAO.getStatus()); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 84053f912..b44e1ae9e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -11,7 +11,7 @@ logging: ### AUTHENTICATION ### authentication: - enabled: true + enabled: false ### DATABASE CONFIGURATION ### diff --git a/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql b/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql index a1c728e96..04c6f3f99 100644 --- a/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql +++ b/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql @@ -1,11 +1,37 @@ +UPDATE experiment +SET algorithms = + ( + SELECT SUBSTR(algorithms, 2, LENGTH(algorithms) - 2) + ); + +UPDATE experiment +SET workflowstatus = 'error' +WHERE workflowstatus IS NULL AND haserror; + +UPDATE experiment +SET workflowstatus = 'completed' +WHERE workflowstatus IS NULL AND NOT haserror; + +UPDATE experiment +SET workflowstatus = 'success' +WHERE workflowstatus = 'completed'; + +UPDATE experiment +SET workflowstatus = 'pending' +WHERE workflowstatus = 'running'; + ALTER TABLE experiment DROP COLUMN haserror, DROP COLUMN hasservererror, DROP COLUMN validations, DROP COLUMN model_slug; +UPDATE experiment +SET algorithms = REPLACE(algorithms, '"name"', '"id"'); ALTER TABLE experiment -RENAME algorithms TO algorithmDetails; +RENAME algorithms TO algorithm; +ALTER TABLE experiment +ALTER COLUMN algorithm TYPE json USING algorithm::json; ALTER TABLE experiment RENAME createdby_username TO created_by_username; ALTER TABLE experiment @@ -16,10 +42,15 @@ ALTER TABLE experiment RENAME workflowstatus TO status; ALTER TABLE experiment -ADD COLUMN updated timestamp without time zone; +ADD COLUMN algorithmId text; + +UPDATE experiment +SET algorithmId = (algorithm ->> 'id'); ALTER TABLE experiment -ADD COLUMN algorithm text; +ALTER COLUMN algorithm TYPE text; +ALTER TABLE experiment +ADD COLUMN updated timestamp without time zone; ALTER TABLE "user" DROP COLUMN birthday, -- GitLab From 943ac2cf83019b8fbfd840a5d19902fd397c208f Mon Sep 17 00:00:00 2001 From: kostas <kostasfilippop@gmail.com> Date: Tue, 29 Dec 2020 08:23:11 -0800 Subject: [PATCH 47/52] Added lombok.Updated claim utils to match the qakeycloak --- pom.xml | 9 +- .../hbp/mip/controllers/PathologiesAPI.java | 3 +- .../eu/hbp/mip/models/DAOs/ExperimentDAO.java | 108 +----------------- .../java/eu/hbp/mip/models/DAOs/UserDAO.java | 41 ++----- .../hbp/mip/services/ActiveUserService.java | 6 +- .../hbp/mip/services/ExperimentService.java | 10 +- .../java/eu/hbp/mip/utils/ClaimUtils.java | 42 ++++--- src/main/resources/application.yml | 2 +- .../migration/V7_0__NewDatabaseStructure.sql | 3 + 9 files changed, 62 insertions(+), 162 deletions(-) diff --git a/pom.xml b/pom.xml index 5d8467960..d92ff1f2d 100644 --- a/pom.xml +++ b/pom.xml @@ -246,7 +246,14 @@ <artifactId>svenson</artifactId> <version>1.5.8</version> </dependency> - </dependencies> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>1.18.16</version> + <scope>provided</scope> + </dependency> + + </dependencies> <build> <finalName>portal-backend</finalName> diff --git a/src/main/java/eu/hbp/mip/controllers/PathologiesAPI.java b/src/main/java/eu/hbp/mip/controllers/PathologiesAPI.java index 175ad2977..5e9a9d344 100644 --- a/src/main/java/eu/hbp/mip/controllers/PathologiesAPI.java +++ b/src/main/java/eu/hbp/mip/controllers/PathologiesAPI.java @@ -70,7 +70,6 @@ public class PathologiesAPI { } logger.LogUserAction("Successfully loaded all authorized pathologies"); - return ResponseEntity.ok().body(ClaimUtils.getAuthorizedPathologies( - activeUserService.getActiveUser().getUsername(), logger, authentication.getAuthorities(), allPathologies)); + return ResponseEntity.ok().body(ClaimUtils.getAuthorizedPathologies(logger, authentication, allPathologies)); } } 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 30d5ee123..e369e5cf6 100644 --- a/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java +++ b/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java @@ -7,6 +7,8 @@ import eu.hbp.mip.models.DTOs.AlgorithmDTO; import eu.hbp.mip.models.DTOs.ExperimentDTO; import eu.hbp.mip.utils.JsonConverters; import io.swagger.annotations.ApiModel; +import lombok.Getter; +import lombok.Setter; import javax.persistence.*; import java.util.*; @@ -15,6 +17,8 @@ import java.util.*; * Created by habfast on 21/04/16. */ @Entity +@Getter +@Setter @Table(name = "`experiment`") @ApiModel @JsonInclude(JsonInclude.Include.NON_NULL) @@ -109,108 +113,4 @@ public class ExperimentDAO { experimentDTO.setViewed(this.viewed); return experimentDTO; } - - public String getAlgorithm() { - return algorithm; - } - - public void setAlgorithm(String algorithm) { - this.algorithm = algorithm; - } - - public String getAlgorithmId() { - return algorithmId; - } - - public void setAlgorithmId(String algorithmId) { - this.algorithmId = algorithmId; - } - - public String getWorkflowHistoryId() { - return workflowHistoryId; - } - - public void setWorkflowHistoryId(String workflowHistoryId) { - this.workflowHistoryId = workflowHistoryId; - } - - public Status getStatus() { - return status; - } - - public void setStatus(Status status) { - this.status = status; - } - - public String getResult() { - return result; - } - - public void setResult(String result) { - this.result = result; - } - - public Date getFinished() { - return finished; - } - - public void setFinished(Date finished) { - this.finished = finished; - } - - public Date getCreated() { - return created; - } - - public void setCreated(Date created) { - this.created = created; - } - - public Date getUpdated() { - return updated; - } - - public void setUpdated(Date updated) { - this.updated = updated; - } - - public UUID getUuid() { - return uuid; - } - - public void setUuid(UUID uuid) { - this.uuid = uuid; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public UserDAO getCreatedBy() { - return createdBy; - } - - public void setCreatedBy(UserDAO createdBy) { - this.createdBy = createdBy; - } - - public boolean isViewed() { - return viewed; - } - - public void setViewed(boolean viewed) { - this.viewed = viewed; - } - - public boolean isShared() { - return shared; - } - - public void setShared(boolean shared) { - this.shared = shared; - } } diff --git a/src/main/java/eu/hbp/mip/models/DAOs/UserDAO.java b/src/main/java/eu/hbp/mip/models/DAOs/UserDAO.java index a94e5da1c..9279b3793 100644 --- a/src/main/java/eu/hbp/mip/models/DAOs/UserDAO.java +++ b/src/main/java/eu/hbp/mip/models/DAOs/UserDAO.java @@ -7,12 +7,16 @@ package eu.hbp.mip.models.DAOs; import com.fasterxml.jackson.annotation.JsonInclude; import com.google.gson.annotations.Expose; import io.swagger.annotations.ApiModel; +import lombok.Getter; +import lombok.Setter; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity +@Getter +@Setter @Table(name = "`user`") @ApiModel @JsonInclude(JsonInclude.Include.NON_NULL) @@ -22,6 +26,9 @@ public class UserDAO { @Expose private String username; + @Expose + private String subjectId; + @Expose private String fullname; @@ -35,42 +42,12 @@ public class UserDAO { // Empty constructor is needed by Hibernate } - public UserDAO(String username, String fullname, String email) { + public UserDAO(String username, String fullname, String email, String subjectId) { this.username = username; this.fullname = fullname; this.email = email; this.agreeNDA = false; - } - - public String getFullname() { - return fullname; - } - - public void setFullname(String fullname) { - this.fullname = fullname; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public Boolean getAgreeNDA() { - return agreeNDA; - } + this.subjectId = subjectId; - public void setAgreeNDA(Boolean agreeNDA) { - this.agreeNDA = agreeNDA; } } diff --git a/src/main/java/eu/hbp/mip/services/ActiveUserService.java b/src/main/java/eu/hbp/mip/services/ActiveUserService.java index f1fe049c2..ee65c170a 100644 --- a/src/main/java/eu/hbp/mip/services/ActiveUserService.java +++ b/src/main/java/eu/hbp/mip/services/ActiveUserService.java @@ -44,17 +44,17 @@ public class ActiveUserService { // If Authentication is OFF, create anonymous user with accepted NDA if (!authenticationIsEnabled) { - user = new UserDAO("anonymous", "anonymous", "anonymous@anonymous.com"); + user = new UserDAO("anonymous", "anonymous", "anonymous@anonymous.com","anonymousId"); user.setAgreeNDA(true); userRepository.save(user); return user; } - //TODO: + // If authentication is ON get user info from Token KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); IDToken idToken = keycloakPrincipal.getKeycloakSecurityContext().getIdToken(); - user = new UserDAO(idToken.getPreferredUsername(), idToken.getName(), idToken.getEmail()); + user = new UserDAO(idToken.getPreferredUsername(), idToken.getName(), idToken.getEmail(), idToken.getId()); UserDAO userInDatabase = userRepository.findByUsername(user.getUsername()); if (userInDatabase == null || !userInDatabase.equals(user)) { diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index 869db8836..2bc16b0f9 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -90,7 +90,7 @@ public class ExperimentService { if (size > 50) throw new BadRequestException("Invalid size input, max size is 50."); Specification<ExperimentDAO> spec; - if(!authenticationIsEnabled || ClaimUtils.validateAccessRightsOnExperiments(user.getUsername(), authentication.getAuthorities(), logger)) + if(!authenticationIsEnabled || ClaimUtils.validateAccessRightsOnExperiments(authentication, logger)) { spec = Specification .where(new ExperimentSpecifications.ExperimentWithName(name)) @@ -147,7 +147,7 @@ public class ExperimentService { !experimentDAO.isShared() && !experimentDAO.getCreatedBy().getUsername().equals(user.getUsername()) && authenticationIsEnabled - && ClaimUtils.validateAccessRightsOnExperiments(user.getUsername(), authentication.getAuthorities(), logger) + && ClaimUtils.validateAccessRightsOnExperiments(authentication, logger) ) { logger.LogUserAction("Accessing Experiment is unauthorized."); throw new UnauthorizedException("You don't have access to the experiment."); @@ -184,7 +184,7 @@ public class ExperimentService { if (authenticationIsEnabled) { String experimentDatasets = getDatasetFromExperimentParameters(experimentDTO, logger); - ClaimUtils.validateAccessRightsOnDatasets(user.getUsername(), authentication.getAuthorities(), experimentDatasets, logger); + ClaimUtils.validateAccessRightsOnDatasets(authentication, experimentDatasets, logger); } // Run with the appropriate engine @@ -222,7 +222,7 @@ public class ExperimentService { if (authenticationIsEnabled) { String experimentDatasets = getDatasetFromExperimentParameters(experimentDTO, logger); - ClaimUtils.validateAccessRightsOnDatasets(user.getUsername(), authentication.getAuthorities(), experimentDatasets, logger); + ClaimUtils.validateAccessRightsOnDatasets(authentication, experimentDatasets, logger); } String body = gson.toJson(algorithmParameters); @@ -739,7 +739,7 @@ public class ExperimentService { if (result == null) { experimentDAO.setStatus(ExperimentDAO.Status.error); } else { - experimentDAO.setResult("[" + result + "]"); + experimentDAO.setResult(result); experimentDAO.setStatus(ExperimentDAO.Status.success); resultFound = true; } diff --git a/src/main/java/eu/hbp/mip/utils/ClaimUtils.java b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java index 501cea1b0..902bf9f9f 100644 --- a/src/main/java/eu/hbp/mip/utils/ClaimUtils.java +++ b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java @@ -3,7 +3,9 @@ package eu.hbp.mip.utils; import com.google.gson.Gson; import eu.hbp.mip.models.DTOs.PathologyDTO; import eu.hbp.mip.utils.Exceptions.UnauthorizedException; -import org.springframework.security.core.GrantedAuthority; +import org.keycloak.KeycloakPrincipal; +import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; +import org.springframework.security.core.Authentication; import java.util.*; @@ -13,26 +15,28 @@ public class ClaimUtils { private static final Gson gson = new Gson(); public static String allDatasetsAllowedClaim() { - return "role_research_dataset_all"; + return "research_dataset_all"; } public static String allExperimentsAllowedClaim() { - return "role_research_experiment_all"; + return "research_experiment_all"; } public static String getDatasetClaim(String datasetCode) { - return "role_research_dataset_" + datasetCode.toLowerCase(); + return "research_dataset_" + datasetCode.toLowerCase(); } - public static void validateAccessRightsOnDatasets(String username, Collection<? extends GrantedAuthority> authorities, - String experimentDatasets, Logger logger) { + public static void validateAccessRightsOnDatasets(Authentication authentication, + String experimentDatasets, Logger logger) { + + ArrayList<String> authorities = getKeycloakAuthorities(authentication); // Don't check for dataset claims if "super" claim exists allowing everything - if (!hasRoleAccess(username, authorities, ClaimUtils.allDatasetsAllowedClaim(), logger)) { + if (!hasRoleAccess(authorities, ClaimUtils.allDatasetsAllowedClaim(), logger)) { for (String dataset : experimentDatasets.split(",")) { String datasetRole = ClaimUtils.getDatasetClaim(dataset); - if (!hasRoleAccess(username, authorities, datasetRole, logger)) { + if (!hasRoleAccess(authorities, datasetRole, logger)) { logger.LogUserAction("You are not allowed to use dataset: " + dataset); throw new UnauthorizedException("You are not authorized to use these datasets."); } @@ -41,19 +45,23 @@ public class ClaimUtils { } } - public static boolean validateAccessRightsOnExperiments(String username, Collection<? extends GrantedAuthority> authorities, Logger logger) { + public static boolean validateAccessRightsOnExperiments(Authentication authentication, Logger logger) { + + ArrayList<String> authorities = getKeycloakAuthorities(authentication); // Check for experiment_all claims - return hasRoleAccess(username, authorities, ClaimUtils.allExperimentsAllowedClaim(), logger); + return hasRoleAccess(authorities, ClaimUtils.allExperimentsAllowedClaim(), logger); } - public static String getAuthorizedPathologies(String username, Logger logger, Collection<? extends GrantedAuthority> authorities, + public static String getAuthorizedPathologies(Logger logger, Authentication authentication, List<PathologyDTO> allPathologies) { // --- Providing only the allowed pathologies/datasets to the user --- logger.LogUserAction("Filter out the unauthorised datasets."); + ArrayList<String> authorities = getKeycloakAuthorities(authentication); + // If the "dataset_all" claim exists then return everything - if (hasRoleAccess(username, authorities, ClaimUtils.allDatasetsAllowedClaim(), logger)) { + if (hasRoleAccess(authorities, ClaimUtils.allDatasetsAllowedClaim(), logger)) { return gson.toJson(allPathologies); } @@ -61,7 +69,7 @@ public class ClaimUtils { for (PathologyDTO curPathology : allPathologies) { List<PathologyDTO.PathologyDatasetDTO> userPathologyDatasets = new ArrayList<>(); for (PathologyDTO.PathologyDatasetDTO dataset : curPathology.getDatasets()) { - if (hasRoleAccess(username, authorities, ClaimUtils.getDatasetClaim(dataset.getCode()), logger)) { + if (hasRoleAccess(authorities, ClaimUtils.getDatasetClaim(dataset.getCode()), logger)) { logger.LogUserAction("Added dataset: " + dataset.getCode()); userPathologyDatasets.add(dataset); } @@ -83,7 +91,7 @@ public class ClaimUtils { return gson.toJson(userPathologies); } - private static boolean hasRoleAccess(String username, Collection<? extends GrantedAuthority> authorities,String role, Logger logger) + private static boolean hasRoleAccess(ArrayList<String> authorities, String role, Logger logger) { List<String> userClaims = Arrays.asList(authorities.toString().toLowerCase() .replaceAll("[\\s+\\]\\[]", "").split(",")); @@ -91,4 +99,10 @@ public class ClaimUtils { logger.LogUserAction("User Claims: " + userClaims); return userClaims.contains(role.toLowerCase()); } + + private static ArrayList<String> getKeycloakAuthorities(Authentication authentication){ + KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) authentication; + KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal) token.getPrincipal(); + return (ArrayList<String>)keycloakPrincipal.getKeycloakSecurityContext().getIdToken().getOtherClaims().get("authorities"); + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b44e1ae9e..84053f912 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -11,7 +11,7 @@ logging: ### AUTHENTICATION ### authentication: - enabled: false + enabled: true ### DATABASE CONFIGURATION ### diff --git a/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql b/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql index 04c6f3f99..c39158269 100644 --- a/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql +++ b/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql @@ -67,6 +67,9 @@ DROP COLUMN team, DROP COLUMN web, DROP COLUMN apikey; +ALTER TABLE "user" +ADD COLUMN subjectID text; + 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"; -- GitLab From 1ea0ceb32c4b7be2df6a97e5b99f81d10b2aab6a Mon Sep 17 00:00:00 2001 From: ThanKarab <tkarabatsis@hotmail.com> Date: Tue, 29 Dec 2020 18:31:16 +0200 Subject: [PATCH 48/52] Added ssl-required. --- docker/config/application.tmpl | 1 + src/main/resources/application.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index 5859f78a8..2cd8bc0c4 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -56,6 +56,7 @@ keycloak: credentials: secret: {{ .Env.KEYCLOAK_CLIENT_SECRET }} principal-attribute: "preferred_username" + ssl-required: {{ .Env.KEYCLOAK_SSL_REQUIRED }} ### EXTERNAL FILES ### # Files are imported when building the docker image diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 84053f912..e77b80358 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -55,6 +55,7 @@ keycloak: credentials: secret: "dae83a6b-c769-4186-8383-f0984c6edf05" principal-attribute: "preferred_username" + ssl-required: none ### EXTERNAL FILES ### -- GitLab From 36ca121d231822d458475d52f1bfd4321428fd6d Mon Sep 17 00:00:00 2001 From: kostas <kostasfilippop@gmail.com> Date: Fri, 8 Jan 2021 12:13:29 -0800 Subject: [PATCH 49/52] Updated result to results to be able to save multiple results and fixed agreedNDA so the property is up to date even when you logout and login again. --- .../configurations/SecurityConfiguration.java | 2 +- .../eu/hbp/mip/models/DAOs/ExperimentDAO.java | 26 ++- .../eu/hbp/mip/models/DTOs/AlgorithmDTO.java | 159 +----------------- .../eu/hbp/mip/models/DTOs/ExperimentDTO.java | 116 +------------ .../eu/hbp/mip/models/DTOs/PathologyDTO.java | 55 +----- .../hbp/mip/services/ActiveUserService.java | 18 +- .../hbp/mip/services/ExperimentService.java | 123 ++++++++------ .../ExperimentSpecifications.java | 2 +- .../java/eu/hbp/mip/utils/ClaimUtils.java | 17 +- src/main/resources/application.yml | 9 +- .../migration/V7_0__NewDatabaseStructure.sql | 6 +- 11 files changed, 152 insertions(+), 381 deletions(-) rename src/main/java/eu/hbp/mip/services/{ => Specifications}/ExperimentSpecifications.java (99%) diff --git a/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java index 2b2e549d4..9f583da4e 100644 --- a/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configurations/SecurityConfiguration.java @@ -56,7 +56,7 @@ public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter "/sso/login", "/v2/api-docs", "/swagger-ui/**", "/swagger-resources/**" // Swagger URLs ).permitAll() - .antMatchers("/galaxy*", "/galaxy/*").hasRole("DATA MANAGER") + .antMatchers("/galaxy*", "/galaxy/*").hasRole("WORKFLOW_ADMIN") .antMatchers("/**").authenticated() .and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository()) .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class); 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 e369e5cf6..a696ab52d 100644 --- a/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java +++ b/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java @@ -52,7 +52,7 @@ public class ExperimentDAO { @Expose @Column(columnDefinition = "TEXT") - private String result; + private String results; @Expose @Column(columnDefinition = "TIMESTAMP WITHOUT TIME ZONE") @@ -95,6 +95,28 @@ public class ExperimentDAO { */ } + + public String getResults() { + return results; + } + + public void setResults(String result) { + this.results = result; + } + + public class ResultObjectDTO { + + ArrayList<Object> result; + + public ArrayList<Object> getResult() { + return this.result; + } + + public void setResult(ArrayList<Object> results) { + this.result = results; + } + } + public ExperimentDTO convertToDTO(boolean includeResult) { ExperimentDTO experimentDTO = new ExperimentDTO(); @@ -105,7 +127,7 @@ public class ExperimentDAO { experimentDTO.setCreatedBy(this.createdBy.getUsername()); experimentDTO.setName(this.name); if(includeResult){ - experimentDTO.setResult(JsonConverters.convertJsonStringToObject(String.valueOf(this.result), new ArrayList<ExperimentDTO.ResultDTO>().getClass())); + experimentDTO.setResults(JsonConverters.convertJsonStringToObject(this.results, new ArrayList<ArrayList<Object>>().getClass())); } experimentDTO.setStatus(this.status); experimentDTO.setShared(this.shared); diff --git a/src/main/java/eu/hbp/mip/models/DTOs/AlgorithmDTO.java b/src/main/java/eu/hbp/mip/models/DTOs/AlgorithmDTO.java index 1a6d8b99d..52db979d5 100644 --- a/src/main/java/eu/hbp/mip/models/DTOs/AlgorithmDTO.java +++ b/src/main/java/eu/hbp/mip/models/DTOs/AlgorithmDTO.java @@ -1,51 +1,15 @@ package eu.hbp.mip.models.DTOs; import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; import java.util.List; +@Getter +@Setter public class AlgorithmDTO { - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDesc() { - return desc; - } - - public void setDesc(String desc) { - this.desc = desc; - } - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public List<AlgorithmParamDTO> getParameters() { - return parameters; - } - - public void setParameters(List<AlgorithmParamDTO> parameters) { - this.parameters = parameters; - } - @SerializedName("name") private String name; @@ -61,6 +25,8 @@ public class AlgorithmDTO { @SerializedName("parameters") private List<AlgorithmParamDTO> parameters; + @Getter + @Setter public static class AlgorithmParamDTO { @SerializedName("name") private String name; @@ -103,118 +69,5 @@ public class AlgorithmDTO { @SerializedName("valueEnumerations") private List<String> valueEnumerations; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDesc() { - return desc; - } - - public void setDesc(String desc) { - this.desc = desc; - } - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getColumnValuesSQLType() { - return columnValuesSQLType; - } - - public void setColumnValuesSQLType(String columnValuesSQLType) { - this.columnValuesSQLType = columnValuesSQLType; - } - - public String getColumnValuesIsCategorical() { - return columnValuesIsCategorical; - } - - public void setColumnValuesIsCategorical(String columnValuesIsCategorical) { - this.columnValuesIsCategorical = columnValuesIsCategorical; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - public String getDefaultValue() { - return defaultValue; - } - - public void setDefaultValue(String defaultValue) { - this.defaultValue = defaultValue; - } - - public String getValueType() { - return valueType; - } - - public void setValueType(String valueType) { - this.valueType = valueType; - } - - public String getValueNotBlank() { - return valueNotBlank; - } - - public void setValueNotBlank(String valueNotBlank) { - this.valueNotBlank = valueNotBlank; - } - - public String getValueMultiple() { - return valueMultiple; - } - - public void setValueMultiple(String valueMultiple) { - this.valueMultiple = valueMultiple; - } - - public String getValueMin() { - return valueMin; - } - - public void setValueMin(String valueMin) { - this.valueMin = valueMin; - } - - public String getValueMax() { - return valueMax; - } - - public void setValueMax(String valueMax) { - this.valueMax = valueMax; - } - - public List<String> getValueEnumerations() { - return valueEnumerations; - } - - public void setValueEnumerations(List<String> valueEnumerations) { - this.valueEnumerations = valueEnumerations; - } } - } 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 2840f669f..34a1bee9c 100644 --- a/src/main/java/eu/hbp/mip/models/DTOs/ExperimentDTO.java +++ b/src/main/java/eu/hbp/mip/models/DTOs/ExperimentDTO.java @@ -2,11 +2,15 @@ package eu.hbp.mip.models.DTOs; import com.fasterxml.jackson.annotation.JsonInclude; import eu.hbp.mip.models.DAOs.ExperimentDAO; +import lombok.Getter; +import lombok.Setter; import java.util.Date; import java.util.List; import java.util.UUID; +@Getter +@Setter @JsonInclude(JsonInclude.Include.NON_NULL) public class ExperimentDTO { @@ -18,121 +22,11 @@ public class ExperimentDTO { private Date finished; private Boolean shared; private Boolean viewed; - private List<ExperimentDTO.ResultDTO> result; + private List<List<Object>> results; private ExperimentDAO.Status status; - private AlgorithmDTO algorithm; public ExperimentDTO() { - } - - public AlgorithmDTO getAlgorithm() { - return algorithm; - } - - public void setAlgorithm(AlgorithmDTO algorithm) { - this.algorithm = algorithm; - } - - public UUID getUuid() { - return uuid; - } - - public void setUuid(UUID uuid) { - this.uuid = uuid; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getCreatedBy() { - return createdBy; - } - - public void setCreatedBy(String createdBy) { - this.createdBy = createdBy; - } - - public Date getCreated() { - return created; - } - - public void setCreated(Date created) { - this.created = created; - } - - public Date getUpdated() { - return updated; - } - - public void setUpdated(Date updated) { - this.updated = updated; - } - - public Date getFinished() { - return finished; - } - - public void setFinished(Date finished) { - this.finished = finished; - } - - public Boolean getShared() { - return shared; - } - - public void setShared(Boolean shared) { - this.shared = shared; - } - - public Boolean getViewed() { - return viewed; - } - - public void setViewed(Boolean viewed) { - this.viewed = viewed; - } - - public List<ExperimentDTO.ResultDTO> getResult() { - return result; - } - - public void setResult(List<ExperimentDTO.ResultDTO> result) { - this.result = result; - } - - public ExperimentDAO.Status getStatus() { - return status; - } - - public void setStatus(ExperimentDAO.Status status) { - this.status = status; - } - - public static class ResultDTO { - - private Object data; - private String type; - - public Object getData() { - return this.data; - } - - public void setData(Object data) { - this.data = data; - } - - public String getType() { - return type; - } - public void setType(String type) { - this.type = type; - } } } diff --git a/src/main/java/eu/hbp/mip/models/DTOs/PathologyDTO.java b/src/main/java/eu/hbp/mip/models/DTOs/PathologyDTO.java index 2a8887b5f..aa78ef8b0 100644 --- a/src/main/java/eu/hbp/mip/models/DTOs/PathologyDTO.java +++ b/src/main/java/eu/hbp/mip/models/DTOs/PathologyDTO.java @@ -1,43 +1,15 @@ package eu.hbp.mip.models.DTOs; import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; import java.util.List; +@Getter +@Setter public class PathologyDTO { - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - - public Object getMetadataHierarchy() { - return metadataHierarchy; - } - - public void setMetadataHierarchy(Object metadataHierarchy) { - this.metadataHierarchy = metadataHierarchy; - } - - public List<PathologyDatasetDTO> getDatasets() { - return datasets; - } - - public void setDatasets(List<PathologyDatasetDTO> datasets) { - this.datasets = datasets; - } - @SerializedName("code") private String code; @@ -50,6 +22,8 @@ public class PathologyDTO { @SerializedName("datasets") private List<PathologyDatasetDTO> datasets; + @Getter + @Setter public static class PathologyDatasetDTO { @SerializedName("code") private String code; @@ -57,22 +31,6 @@ public class PathologyDTO { @SerializedName("label") private String label; - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - public String toString() { return code; } @@ -81,5 +39,4 @@ public class PathologyDTO { public String toString() { return code; } - } diff --git a/src/main/java/eu/hbp/mip/services/ActiveUserService.java b/src/main/java/eu/hbp/mip/services/ActiveUserService.java index ee65c170a..bc3c8ced7 100644 --- a/src/main/java/eu/hbp/mip/services/ActiveUserService.java +++ b/src/main/java/eu/hbp/mip/services/ActiveUserService.java @@ -4,15 +4,14 @@ import eu.hbp.mip.models.DAOs.UserDAO; import eu.hbp.mip.repositories.UserRepository; import org.keycloak.KeycloakPrincipal; import org.keycloak.representations.IDToken; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; -import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import javax.inject.Named; +import java.util.Objects; @Component @Named("ActiveUserService") @@ -44,7 +43,7 @@ public class ActiveUserService { // If Authentication is OFF, create anonymous user with accepted NDA if (!authenticationIsEnabled) { - user = new UserDAO("anonymous", "anonymous", "anonymous@anonymous.com","anonymousId"); + user = new UserDAO("anonymous", "anonymous", "anonymous@anonymous.com", "anonymousId"); user.setAgreeNDA(true); userRepository.save(user); return user; @@ -57,9 +56,20 @@ public class ActiveUserService { user = new UserDAO(idToken.getPreferredUsername(), idToken.getName(), idToken.getEmail(), idToken.getId()); UserDAO userInDatabase = userRepository.findByUsername(user.getUsername()); - if (userInDatabase == null || !userInDatabase.equals(user)) { + if (userInDatabase == null) { userRepository.save(user); + return user; + } + + if (!Objects.equals(user.getEmail(),userInDatabase.getEmail()) + || !Objects.equals(user.getFullname(),userInDatabase.getFullname()) + ) { + userInDatabase.setFullname(user.getFullname()); + userInDatabase.setEmail(user.getEmail()); } + + user = userInDatabase; + userRepository.save(user); return user; } diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index 2bc16b0f9..1ef7c09d7 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -10,6 +10,7 @@ import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.google.gson.internal.LinkedTreeMap; import eu.hbp.mip.controllers.galaxy.retrofit.RetroFitGalaxyClients; import eu.hbp.mip.controllers.galaxy.retrofit.RetrofitClientInstance; import eu.hbp.mip.models.DAOs.ExperimentDAO; @@ -19,6 +20,7 @@ import eu.hbp.mip.models.DTOs.ExperimentDTO; import eu.hbp.mip.models.galaxy.GalaxyWorkflowResult; import eu.hbp.mip.models.galaxy.PostWorkflowToGalaxyDtoResponse; import eu.hbp.mip.repositories.ExperimentRepository; +import eu.hbp.mip.services.Specifications.ExperimentSpecifications; import eu.hbp.mip.utils.ClaimUtils; import eu.hbp.mip.utils.Exceptions.*; import eu.hbp.mip.utils.HTTPUtil; @@ -211,6 +213,14 @@ public class ExperimentService { //Checking if check (POST) /experiments has proper input. checkPostExperimentProperInput(experimentDTO, logger); + // Get the type and name of algorithm + String algorithmType = experimentDTO.getAlgorithm().getType(); + + if(algorithmType == "workflow"){ + logger.LogUserAction("You can not run workflow algorithms transiently."); + throw new BadRequestException("You can not run workflow algorithms transiently."); + } + // Get the parameters List<AlgorithmDTO.AlgorithmParamDTO> algorithmParameters = experimentDTO.getAlgorithm().getParameters(); @@ -236,7 +246,7 @@ public class ExperimentService { logger.LogUserAction("Experiment with uuid: " + experimentDTO.getUuid() + "gave response code: " + exaremeResult.getCode() + " and result: " + exaremeResult.getResults()); - experimentDTO.setResult((exaremeResult.getCode() >= 400) ? null : exaremeResult.getResults()); + experimentDTO.setResults((exaremeResult.getCode() >= 400) ? null : JsonConverters.convertJsonStringToObject(exaremeResult.getResults(), new ArrayList<ArrayList<Object>>().getClass())); experimentDTO.setStatus((exaremeResult.getCode() >= 400) ? ExperimentDAO.Status.error : ExperimentDAO.Status.success); return experimentDTO; @@ -244,10 +254,10 @@ public class ExperimentService { /** * The updateExperiment will update the experiment's properties - * * @param uuid is the id of the experiment to be updated * @param experimentDTO is the experiment information to be updated * @param logger contains username and the endpoint. + * @return */ public ExperimentDTO updateExperiment(String uuid, ExperimentDTO experimentDTO, Logger logger) { ExperimentDAO experimentDAO; @@ -321,7 +331,7 @@ public class ExperimentService { && experimentDTO.getViewed() == null && experimentDTO.getCreated() == null && experimentDTO.getCreatedBy() == null - && experimentDTO.getResult() == null + && experimentDTO.getResults() == null && experimentDTO.getStatus() == null && experimentDTO.getUuid() == null; @@ -362,7 +372,7 @@ public class ExperimentService { throw new BadRequestException("Finished is not editable."); } - if (experimentDTO.getResult() != null) { + if (experimentDTO.getResults() != null) { logger.LogUserAction( "Result is not editable."); throw new BadRequestException("Result is not editable."); } @@ -497,6 +507,50 @@ public class ExperimentService { } } + private String formattingExaremeResult(String result) { + List<LinkedTreeMap<String,Object>> jsonObject = JsonConverters.convertJsonStringToObject(result, new ArrayList<ExperimentDAO.ResultObjectDTO>().getClass()); + LinkedTreeMap<String,Object> firstResult = jsonObject.get(0); + return "[" + JsonConverters.convertObjectToJsonString(firstResult.get("result")) + "]"; + } + + private String formattingGalaxyResult(String result) { + List<LinkedTreeMap<String,Object>> jsonObjects = JsonConverters.convertJsonStringToObject(result, new ArrayList<ExperimentDAO.ResultObjectDTO>().getClass()); + List<Object> objects = new ArrayList<>(); + for (int i = 0; i < jsonObjects.size(); i++) { + LinkedTreeMap<String,Object> k = jsonObjects.get(i); + objects.add(k.get("result")); + } + return JsonConverters.convertObjectToJsonString(objects); + } + + + /** + * The runExaremeExperiment will run to exareme the experiment + * + * @param url is the url that contain the results of the experiment + * @param body is the parameters of the algorithm + * @param experimentDTO is the experiment information to be executed in the exareme + * @return the result of exareme as well as the http status that was retrieved + */ + private ExaremeResult runExaremeExperiment(String url, String body, ExperimentDTO experimentDTO) { + + StringBuilder results = new StringBuilder(); + int code; + try { + code = HTTPUtil.sendPost(url, body, results); + } catch (Exception e) { + throw new InternalServerError("Error occurred : " + e.getMessage()); + } + + String resultToMatchGalaxy = "[" + results + "]"; + String formattedResult = formattingExaremeResult(resultToMatchGalaxy); + Logger.LogExperimentAction(experimentDTO.getName(), experimentDTO.getUuid(), "Algorithm finished with code: " + code + "and result :" + formattedResult); + + return new ExaremeResult(code, formattedResult); + } + + + /* -------------------------------------- EXAREME CALLS ---------------------------------------------------------*/ /** @@ -539,7 +593,7 @@ public class ExperimentService { Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Experiment with uuid: " + experimentDAO.getUuid() + "gave response code: " + exaremeResult.getCode() + " and result: " + exaremeResult.getResults()); - experimentDAO.setResult((exaremeResult.getCode() >= 400) ? null : JsonConverters.convertObjectToJsonString(exaremeResult.getResults())); + experimentDAO.setResults((exaremeResult.getCode() >= 400) ? null : exaremeResult.getResults()); experimentDAO.setStatus((exaremeResult.getCode() >= 400) ? ExperimentDAO.Status.error : ExperimentDAO.Status.success); } catch (Exception e) { Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "There was an exception: " + e.getMessage()); @@ -554,32 +608,6 @@ public class ExperimentService { return experimentDTO; } - /** - * The runExaremeExperiment will run to exareme the experiment - * - * @param url is the url that contain the results of the experiment - * @param body is the parameters of the algorithm - * @param experimentDTO is the experiment information to be executed in the exareme - * @return the result of exareme as well as the http status that was retrieved - */ - public ExaremeResult runExaremeExperiment(String url, String body, ExperimentDTO experimentDTO) { - - StringBuilder results = new StringBuilder(); - int code; - try { - code = HTTPUtil.sendPost(url, body, results); - } catch (Exception e) { - throw new InternalServerError("Error occurred : " + e.getMessage()); - } - Logger.LogExperimentAction(experimentDTO.getName(), experimentDTO.getUuid(), "Algorithm finished with code: " + code); - - // Results are stored in the experiment object - ExperimentDTO experimentDTOWithOnlyResult = JsonConverters.convertJsonStringToObject(String.valueOf(results), ExperimentDTO.class); - List<ExperimentDTO.ResultDTO> resultDTOS = experimentDTOWithOnlyResult.getResult(); - return new ExaremeResult(code, resultDTOS); - } - - /* --------------------------------------- GALAXY CALLS ---------------------------------------------------------*/ @@ -718,12 +746,12 @@ public class ExperimentService { Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "State is: " + state); switch (state) { - case "running": + case "pending": // Do nothing, when the experiment is created the status is set to running Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Workflow is still running."); break; - case "completed": + case "success": // Get only the job result that is visible List<GalaxyWorkflowResult> workflowJobsResults = getWorkflowResults(experimentDAO); Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Results are: " + workflowJobsResults.toString()); @@ -739,7 +767,7 @@ public class ExperimentService { if (result == null) { experimentDAO.setStatus(ExperimentDAO.Status.error); } else { - experimentDAO.setResult(result); + experimentDAO.setResults(result); experimentDAO.setStatus(ExperimentDAO.Status.success); resultFound = true; } @@ -790,8 +818,8 @@ public class ExperimentService { } // If result exists return - if (experimentDAO.getResult() != null) { - Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "ResultDTO exists: " + experimentDAO.getResult()); + if (experimentDAO.getResults() != null) { + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "ResultDTO exists: " + experimentDAO.getResults()); return; } } @@ -801,10 +829,10 @@ public class ExperimentService { /** * @param experimentDAO The experiment of the workflow - * @return "running" -> When the workflow is still running + * @return "pending" -> When the workflow is still running * "internalError" -> When an exception or a bad request occurred * "error" -> When the workflow produced an error - * "completed" -> When the workflow completed successfully + * "success" -> When the workflow completed successfully */ public String getWorkflowStatus(ExperimentDAO experimentDAO) { String historyId = experimentDAO.getWorkflowHistoryId(); @@ -846,14 +874,14 @@ public class ExperimentService { Logger.LogExperimentAction(experimentName, experimentId, " Completed!"); switch (state) { case "ok": - return "completed"; + return "success"; case "error": return "error"; - case "running": + case "pending": case "new": case "waiting": case "queued": - return "running"; + return "pending"; default: return "internalError"; } @@ -919,7 +947,8 @@ public class ExperimentService { + response.code() + "" + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); return null; } - resultJson = new Gson().toJson(response.body()); + + resultJson = gson.toJson(response.body()); Logger.LogExperimentAction(experimentName, experimentId, " ResultDTO: " + resultJson); } catch (IOException e) { @@ -928,8 +957,8 @@ public class ExperimentService { return null; } - Logger.LogExperimentAction(experimentName, experimentId, " Completed!"); - return resultJson; + + return formattingGalaxyResult(resultJson); } @@ -979,9 +1008,9 @@ public class ExperimentService { static class ExaremeResult { private int code; - private List<ExperimentDTO.ResultDTO> results; + private String results; - public ExaremeResult(int code, List<ExperimentDTO.ResultDTO> results) { + public ExaremeResult(int code, String results) { this.code = code; this.results = results; } @@ -990,7 +1019,7 @@ public class ExperimentService { return code; } - public List<ExperimentDTO.ResultDTO> getResults() { + public String getResults() { return results; } } diff --git a/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java b/src/main/java/eu/hbp/mip/services/Specifications/ExperimentSpecifications.java similarity index 99% rename from src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java rename to src/main/java/eu/hbp/mip/services/Specifications/ExperimentSpecifications.java index e5b51a692..c1e6a13ac 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentSpecifications.java +++ b/src/main/java/eu/hbp/mip/services/Specifications/ExperimentSpecifications.java @@ -1,4 +1,4 @@ -package eu.hbp.mip.services; +package eu.hbp.mip.services.Specifications; import eu.hbp.mip.models.DAOs.ExperimentDAO; import eu.hbp.mip.models.DAOs.UserDAO; diff --git a/src/main/java/eu/hbp/mip/utils/ClaimUtils.java b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java index 902bf9f9f..f5676b52c 100644 --- a/src/main/java/eu/hbp/mip/utils/ClaimUtils.java +++ b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java @@ -2,6 +2,7 @@ package eu.hbp.mip.utils; import com.google.gson.Gson; import eu.hbp.mip.models.DTOs.PathologyDTO; +import eu.hbp.mip.utils.Exceptions.InternalServerError; import eu.hbp.mip.utils.Exceptions.UnauthorizedException; import org.keycloak.KeycloakPrincipal; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; @@ -29,7 +30,7 @@ public class ClaimUtils { public static void validateAccessRightsOnDatasets(Authentication authentication, String experimentDatasets, Logger logger) { - ArrayList<String> authorities = getKeycloakAuthorities(authentication); + ArrayList<String> authorities = getKeycloakAuthorities(authentication, logger); // Don't check for dataset claims if "super" claim exists allowing everything if (!hasRoleAccess(authorities, ClaimUtils.allDatasetsAllowedClaim(), logger)) { @@ -47,7 +48,7 @@ public class ClaimUtils { public static boolean validateAccessRightsOnExperiments(Authentication authentication, Logger logger) { - ArrayList<String> authorities = getKeycloakAuthorities(authentication); + ArrayList<String> authorities = getKeycloakAuthorities(authentication, logger); // Check for experiment_all claims return hasRoleAccess(authorities, ClaimUtils.allExperimentsAllowedClaim(), logger); @@ -58,7 +59,7 @@ public class ClaimUtils { // --- Providing only the allowed pathologies/datasets to the user --- logger.LogUserAction("Filter out the unauthorised datasets."); - ArrayList<String> authorities = getKeycloakAuthorities(authentication); + ArrayList<String> authorities = getKeycloakAuthorities(authentication, logger); // If the "dataset_all" claim exists then return everything if (hasRoleAccess(authorities, ClaimUtils.allDatasetsAllowedClaim(), logger)) { @@ -100,9 +101,15 @@ public class ClaimUtils { return userClaims.contains(role.toLowerCase()); } - private static ArrayList<String> getKeycloakAuthorities(Authentication authentication){ + private static ArrayList<String> getKeycloakAuthorities(Authentication authentication, Logger logger){ KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) authentication; KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal) token.getPrincipal(); - return (ArrayList<String>)keycloakPrincipal.getKeycloakSecurityContext().getIdToken().getOtherClaims().get("authorities"); + if(keycloakPrincipal.getKeycloakSecurityContext().getIdToken().getOtherClaims().get("authorities") == null) + { + logger.LogUserAction("Your user has no roles."); + throw new InternalServerError("Your user has no roles."); + } + + return (ArrayList<String>)keycloakPrincipal.getKeycloakSecurityContext().getIdToken().getOtherClaims().get("authorities"); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e77b80358..58ecf45e1 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -11,7 +11,7 @@ logging: ### AUTHENTICATION ### authentication: - enabled: true + enabled: false ### DATABASE CONFIGURATION ### @@ -48,14 +48,13 @@ services: ### KEYCLOAK ### keycloak: enabled: true - auth-server-url: "http://127.0.0.1/auth" + auth-server-url: "https://iam.humanbrainproject.eu/auth" realm: "MIP" - resource: "MIP" + resource: "mipfedqa" enable-basic-auth: true credentials: - secret: "dae83a6b-c769-4186-8383-f0984c6edf05" + secret: "492ddb7d-a7c6-4143-89ae-dbfa6f970aca" principal-attribute: "preferred_username" - ssl-required: none ### EXTERNAL FILES ### diff --git a/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql b/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql index c39158269..6a3b0c364 100644 --- a/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql +++ b/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql @@ -26,8 +26,8 @@ DROP COLUMN hasservererror, DROP COLUMN validations, DROP COLUMN model_slug; -UPDATE experiment -SET algorithms = REPLACE(algorithms, '"name"', '"id"'); +ALTER TABLE experiment +RENAME result TO results; ALTER TABLE experiment RENAME algorithms TO algorithm; ALTER TABLE experiment @@ -45,7 +45,7 @@ ALTER TABLE experiment ADD COLUMN algorithmId text; UPDATE experiment -SET algorithmId = (algorithm ->> 'id'); +SET algorithmId = (algorithm ->> 'name'); ALTER TABLE experiment ALTER COLUMN algorithm TYPE text; -- GitLab From 854b1853e9f1637b7b808dc7148fffa8061d7cbb Mon Sep 17 00:00:00 2001 From: kostas <kostasfilippop@gmail.com> Date: Mon, 11 Jan 2021 08:06:17 -0800 Subject: [PATCH 50/52] Reverting results to result and accessing only the first result from galaxy. --- .../eu/hbp/mip/models/DAOs/ExperimentDAO.java | 26 +------ .../eu/hbp/mip/models/DTOs/ExperimentDTO.java | 3 +- .../hbp/mip/services/ExperimentService.java | 71 +++++++------------ .../migration/V7_0__NewDatabaseStructure.sql | 2 - 4 files changed, 30 insertions(+), 72 deletions(-) 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 a696ab52d..585bd8bd6 100644 --- a/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java +++ b/src/main/java/eu/hbp/mip/models/DAOs/ExperimentDAO.java @@ -52,7 +52,7 @@ public class ExperimentDAO { @Expose @Column(columnDefinition = "TEXT") - private String results; + private String result; @Expose @Column(columnDefinition = "TIMESTAMP WITHOUT TIME ZONE") @@ -95,28 +95,6 @@ public class ExperimentDAO { */ } - - public String getResults() { - return results; - } - - public void setResults(String result) { - this.results = result; - } - - public class ResultObjectDTO { - - ArrayList<Object> result; - - public ArrayList<Object> getResult() { - return this.result; - } - - public void setResult(ArrayList<Object> results) { - this.result = results; - } - } - public ExperimentDTO convertToDTO(boolean includeResult) { ExperimentDTO experimentDTO = new ExperimentDTO(); @@ -127,7 +105,7 @@ public class ExperimentDAO { experimentDTO.setCreatedBy(this.createdBy.getUsername()); experimentDTO.setName(this.name); if(includeResult){ - experimentDTO.setResults(JsonConverters.convertJsonStringToObject(this.results, new ArrayList<ArrayList<Object>>().getClass())); + experimentDTO.setResult(JsonConverters.convertJsonStringToObject(String.valueOf(this.result), new ArrayList<>().getClass())); } experimentDTO.setStatus(this.status); experimentDTO.setShared(this.shared); 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 34a1bee9c..e64c6f75e 100644 --- a/src/main/java/eu/hbp/mip/models/DTOs/ExperimentDTO.java +++ b/src/main/java/eu/hbp/mip/models/DTOs/ExperimentDTO.java @@ -22,11 +22,10 @@ public class ExperimentDTO { private Date finished; private Boolean shared; private Boolean viewed; - private List<List<Object>> results; + private List<Object> result; private ExperimentDAO.Status status; private AlgorithmDTO algorithm; public ExperimentDTO() { - } } diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index 1ef7c09d7..bf989c4ac 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -169,7 +169,6 @@ public class ExperimentService { * @return the experiment information which was created */ public ExperimentDTO createExperiment(Authentication authentication, ExperimentDTO experimentDTO, Logger logger) { - UserDAO user = activeUserService.getActiveUser(); //Checking if check (POST) /experiments has proper input. checkPostExperimentProperInput(experimentDTO, logger); @@ -208,7 +207,6 @@ public class ExperimentService { * @return the experiment information which was created */ public ExperimentDTO createTransientExperiment(Authentication authentication, ExperimentDTO experimentDTO, Logger logger) { - UserDAO user = activeUserService.getActiveUser(); //Checking if check (POST) /experiments has proper input. checkPostExperimentProperInput(experimentDTO, logger); @@ -216,7 +214,7 @@ public class ExperimentService { // Get the type and name of algorithm String algorithmType = experimentDTO.getAlgorithm().getType(); - if(algorithmType == "workflow"){ + if(algorithmType.equals("workflow")){ logger.LogUserAction("You can not run workflow algorithms transiently."); throw new BadRequestException("You can not run workflow algorithms transiently."); } @@ -246,7 +244,7 @@ public class ExperimentService { logger.LogUserAction("Experiment with uuid: " + experimentDTO.getUuid() + "gave response code: " + exaremeResult.getCode() + " and result: " + exaremeResult.getResults()); - experimentDTO.setResults((exaremeResult.getCode() >= 400) ? null : JsonConverters.convertJsonStringToObject(exaremeResult.getResults(), new ArrayList<ArrayList<Object>>().getClass())); + experimentDTO.setResult((exaremeResult.getCode() >= 400) ? null : exaremeResult.getResults()); experimentDTO.setStatus((exaremeResult.getCode() >= 400) ? ExperimentDAO.Status.error : ExperimentDAO.Status.success); return experimentDTO; @@ -254,10 +252,10 @@ public class ExperimentService { /** * The updateExperiment will update the experiment's properties + * * @param uuid is the id of the experiment to be updated * @param experimentDTO is the experiment information to be updated * @param logger contains username and the endpoint. - * @return */ public ExperimentDTO updateExperiment(String uuid, ExperimentDTO experimentDTO, Logger logger) { ExperimentDAO experimentDAO; @@ -331,7 +329,7 @@ public class ExperimentService { && experimentDTO.getViewed() == null && experimentDTO.getCreated() == null && experimentDTO.getCreatedBy() == null - && experimentDTO.getResults() == null + && experimentDTO.getResult() == null && experimentDTO.getStatus() == null && experimentDTO.getUuid() == null; @@ -372,7 +370,7 @@ public class ExperimentService { throw new BadRequestException("Finished is not editable."); } - if (experimentDTO.getResults() != null) { + if (experimentDTO.getResult() != null) { logger.LogUserAction( "Result is not editable."); throw new BadRequestException("Result is not editable."); } @@ -507,23 +505,15 @@ public class ExperimentService { } } - private String formattingExaremeResult(String result) { - List<LinkedTreeMap<String,Object>> jsonObject = JsonConverters.convertJsonStringToObject(result, new ArrayList<ExperimentDAO.ResultObjectDTO>().getClass()); - LinkedTreeMap<String,Object> firstResult = jsonObject.get(0); - return "[" + JsonConverters.convertObjectToJsonString(firstResult.get("result")) + "]"; - } - private String formattingGalaxyResult(String result) { - List<LinkedTreeMap<String,Object>> jsonObjects = JsonConverters.convertJsonStringToObject(result, new ArrayList<ExperimentDAO.ResultObjectDTO>().getClass()); - List<Object> objects = new ArrayList<>(); - for (int i = 0; i < jsonObjects.size(); i++) { - LinkedTreeMap<String,Object> k = jsonObjects.get(i); - objects.add(k.get("result")); - } - return JsonConverters.convertObjectToJsonString(objects); + List<LinkedTreeMap<String,Object>> jsonObject = JsonConverters.convertJsonStringToObject(result, new ArrayList<ArrayList<Object>>().getClass()); + LinkedTreeMap<String,Object> firstResult = jsonObject.get(0); + jsonObject = (List<LinkedTreeMap<String, Object>>) firstResult.get("result"); + List<LinkedTreeMap<String,Object>> finalJsonObject = new ArrayList<>(); + finalJsonObject.add(jsonObject.get(0)); + return JsonConverters.convertObjectToJsonString(finalJsonObject); } - /** * The runExaremeExperiment will run to exareme the experiment * @@ -532,7 +522,7 @@ public class ExperimentService { * @param experimentDTO is the experiment information to be executed in the exareme * @return the result of exareme as well as the http status that was retrieved */ - private ExaremeResult runExaremeExperiment(String url, String body, ExperimentDTO experimentDTO) { + public ExaremeResult runExaremeExperiment(String url, String body, ExperimentDTO experimentDTO) { StringBuilder results = new StringBuilder(); int code; @@ -542,15 +532,14 @@ public class ExperimentService { throw new InternalServerError("Error occurred : " + e.getMessage()); } - String resultToMatchGalaxy = "[" + results + "]"; - String formattedResult = formattingExaremeResult(resultToMatchGalaxy); - Logger.LogExperimentAction(experimentDTO.getName(), experimentDTO.getUuid(), "Algorithm finished with code: " + code + "and result :" + formattedResult); + // Results are stored in the experiment object + ExperimentDTO experimentDTOWithOnlyResult = JsonConverters.convertJsonStringToObject(String.valueOf(results), ExperimentDTO.class); + List<Object> resultDTOS = experimentDTOWithOnlyResult.getResult(); + Logger.LogExperimentAction(experimentDTO.getName(), experimentDTO.getUuid(), "Thread named :" + Thread.currentThread().getName() + " with id :" + Thread.currentThread().getId() + " started!"); - return new ExaremeResult(code, formattedResult); + return new ExaremeResult(code, resultDTOS); } - - /* -------------------------------------- EXAREME CALLS ---------------------------------------------------------*/ /** @@ -593,7 +582,7 @@ public class ExperimentService { Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "Experiment with uuid: " + experimentDAO.getUuid() + "gave response code: " + exaremeResult.getCode() + " and result: " + exaremeResult.getResults()); - experimentDAO.setResults((exaremeResult.getCode() >= 400) ? null : exaremeResult.getResults()); + experimentDAO.setResult((exaremeResult.getCode() >= 400) ? null : JsonConverters.convertObjectToJsonString(exaremeResult.getResults())); experimentDAO.setStatus((exaremeResult.getCode() >= 400) ? ExperimentDAO.Status.error : ExperimentDAO.Status.success); } catch (Exception e) { Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "There was an exception: " + e.getMessage()); @@ -685,11 +674,6 @@ public class ExperimentService { } else { // Something unexpected happened String msgErr = gson.toJson(response.errorBody()); logger.LogUserAction("Error Response: " + msgErr); - - // Values are read from streams. - JSONObject jObjectError = new JSONObject(msgErr); - String errMsg = jObjectError.get("err_msg").toString(); - experimentDTO.setStatus((response.code() >= 400) ? ExperimentDAO.Status.error : ExperimentDAO.Status.success); } @@ -767,7 +751,7 @@ public class ExperimentService { if (result == null) { experimentDAO.setStatus(ExperimentDAO.Status.error); } else { - experimentDAO.setResults(result); + experimentDAO.setResult(result); experimentDAO.setStatus(ExperimentDAO.Status.success); resultFound = true; } @@ -818,8 +802,8 @@ public class ExperimentService { } // If result exists return - if (experimentDAO.getResults() != null) { - Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "ResultDTO exists: " + experimentDAO.getResults()); + if (experimentDAO.getResult() != null) { + Logger.LogExperimentAction(experimentDAO.getName(), experimentDAO.getUuid(), "ResultDTO exists: " + experimentDAO.getResult()); return; } } @@ -947,8 +931,7 @@ public class ExperimentService { + response.code() + "" + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); return null; } - - resultJson = gson.toJson(response.body()); + resultJson = new Gson().toJson(response.body()); Logger.LogExperimentAction(experimentName, experimentId, " ResultDTO: " + resultJson); } catch (IOException e) { @@ -957,7 +940,7 @@ public class ExperimentService { return null; } - + Logger.LogExperimentAction(experimentName, experimentId, " Completed!"); return formattingGalaxyResult(resultJson); } @@ -1007,10 +990,10 @@ public class ExperimentService { } static class ExaremeResult { - private int code; - private String results; + private final int code; + private final List<Object> results; - public ExaremeResult(int code, String results) { + public ExaremeResult(int code, List<Object> results) { this.code = code; this.results = results; } @@ -1019,7 +1002,7 @@ public class ExperimentService { return code; } - public String getResults() { + public List<Object> getResults() { return results; } } diff --git a/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql b/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql index 6a3b0c364..e4791c68c 100644 --- a/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql +++ b/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql @@ -26,8 +26,6 @@ DROP COLUMN hasservererror, DROP COLUMN validations, DROP COLUMN model_slug; -ALTER TABLE experiment -RENAME result TO results; ALTER TABLE experiment RENAME algorithms TO algorithm; ALTER TABLE experiment -- GitLab From 68ff221abe0fa896029c839245d3ce6b52ed2f01 Mon Sep 17 00:00:00 2001 From: kostas <kostasfilippop@gmail.com> Date: Thu, 14 Jan 2021 04:49:03 -0800 Subject: [PATCH 51/52] Remove a log and small change to 7.0 migration script. --- src/main/java/eu/hbp/mip/services/ExperimentService.java | 1 - src/main/resources/application.yml | 2 +- src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/hbp/mip/services/ExperimentService.java b/src/main/java/eu/hbp/mip/services/ExperimentService.java index bf989c4ac..45345c8a5 100644 --- a/src/main/java/eu/hbp/mip/services/ExperimentService.java +++ b/src/main/java/eu/hbp/mip/services/ExperimentService.java @@ -535,7 +535,6 @@ public class ExperimentService { // Results are stored in the experiment object ExperimentDTO experimentDTOWithOnlyResult = JsonConverters.convertJsonStringToObject(String.valueOf(results), ExperimentDTO.class); List<Object> resultDTOS = experimentDTOWithOnlyResult.getResult(); - Logger.LogExperimentAction(experimentDTO.getName(), experimentDTO.getUuid(), "Thread named :" + Thread.currentThread().getName() + " with id :" + Thread.currentThread().getId() + " started!"); return new ExaremeResult(code, resultDTOS); } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 58ecf45e1..345fc4bfa 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -53,7 +53,7 @@ keycloak: resource: "mipfedqa" enable-basic-auth: true credentials: - secret: "492ddb7d-a7c6-4143-89ae-dbfa6f970aca" + secret: "dae83a6b-c769-4186-8383-f0984c6edf05" principal-attribute: "preferred_username" diff --git a/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql b/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql index e4791c68c..c1732c9e9 100644 --- a/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql +++ b/src/main/resources/db/migration/V7_0__NewDatabaseStructure.sql @@ -9,7 +9,7 @@ SET workflowstatus = 'error' WHERE workflowstatus IS NULL AND haserror; UPDATE experiment -SET workflowstatus = 'completed' +SET workflowstatus = 'success' WHERE workflowstatus IS NULL AND NOT haserror; UPDATE experiment -- GitLab From 2ab92b77ba59b7982e64435bc839937964a2190a Mon Sep 17 00:00:00 2001 From: kostas <kostasfilippop@gmail.com> Date: Fri, 15 Jan 2021 10:20:40 -0800 Subject: [PATCH 52/52] Set the ssl-required to none --- docker/config/application.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index 2cd8bc0c4..ae10744aa 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -56,7 +56,7 @@ keycloak: credentials: secret: {{ .Env.KEYCLOAK_CLIENT_SECRET }} principal-attribute: "preferred_username" - ssl-required: {{ .Env.KEYCLOAK_SSL_REQUIRED }} + ssl-required: none ### EXTERNAL FILES ### # Files are imported when building the docker image -- GitLab