diff --git a/docker/README.md b/docker/README.md index cc8c59fe1d79a1a50fcfd566c439cc9d8c5c90e4..da8299413287a703b38d12c233e8abb0c32babfa 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 e50a9bf47d18a7f21586f50abbb61f127283ba25..5292dffd056dbb97d42ea3f766101386b0c9149f 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 d2a0dd739711bcd9d95240e3d0fe09f4c8291635..22f904af5f97e70d45925434940e594f772df498 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 d5dca19ae3b3503876b884dabdf48e7a2d808898..51fc1b4857ad777bcfb7461e0314b09537e37559 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 322f33f4f5a8a08dff189b9e76a332e2983317bb..d5fc161b11dab3b68bbc22932df82e229a54f8a0 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 6c89e1b941aef37822f945d2d0e90ae1830d0c5d..055b04b2363e5522f244d5319197275993b79643 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 9d2b66492f92a3051c189263acfc1437f9a4d42b..696fac039275ae760711ac6d9ee7350c15691f47 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 886ad1ac90776e3c8b66025a6f7a22c223eadb6a..66f34b670bfaa0c3db99e416064b638d23103e8f 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 0d0ba246ded6f16424b548d79252434c11a79361..0e1e9a9ff49408a4cfa034a6262e48f6001e8ef7 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 4253ae7dd859ed63a47ffba68a9bcafc5646302c..0000000000000000000000000000000000000000 --- 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 617b25a5f76cf8fbd321d72b9b154bd5c7196fe0..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..d925ce3f50bc4cb83b533c25253771a5de27306f --- /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 d11971ddd28b6d931c9f60bbef29512286d99c5f..7ffffc7ebfca5f53cbd840cf259b8643667fa335 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 0000000000000000000000000000000000000000..65ab563a91ca742cb81d154592a5e2f8bf81e26e --- /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 0000000000000000000000000000000000000000..84be80128541a7df71b99443e46b2a72259d7a58 --- /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 }}