diff --git a/build.sh b/build.sh index 84efdf7b37f26e7a7083bbb402aeced4609a152e..ef4f10565705b3eb3d0fae54b60de23671f356bf 100755 --- a/build.sh +++ b/build.sh @@ -38,6 +38,7 @@ docker build --build-arg BUILD_DATE=$(date -Iseconds) \ . + BUGSNAG_KEY="" eval $(grep -e "^\\s*BUGSNAG_KEY" Dockerfile | tr '\\' ' ') diff --git a/pom.xml b/pom.xml index fb47d331018bbd301afd6350b5136c2f5f2020e5..20a6f066122779eab771ca0d012dbcc4a8e8a208 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,6 @@ <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> - </dependency> <dependency> <groupId>org.springframework.boot</groupId> diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index a92879577926739b703e4d015a1a64caa1ed5b87..adc3df815fbc74d27f78e8bbf661ff31fc168a89 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -1,10 +1,7 @@ package eu.hbp.mip.configuration; import eu.hbp.mip.model.UserInfo; -import eu.hbp.mip.utils.CORSFilter; -import eu.hbp.mip.utils.CustomLoginUrlAuthenticationEntryPoint; -import eu.hbp.mip.utils.HTTPUtil; -import eu.hbp.mip.utils.UserActionLogging; +import eu.hbp.mip.utils.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -16,14 +13,17 @@ import org.springframework.boot.context.embedded.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.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.OAuth2ProtectedResourceDetails; +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.web.access.channel.ChannelProcessingFilter; @@ -34,13 +34,13 @@ 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; -import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; - +import javax.net.ssl.*; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; @@ -48,34 +48,14 @@ 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; -//newlyadded for logout -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestTemplate; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import java.net.URI; - - -import java.security.SecureRandom; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; - // See https://spring.io/guides/tutorials/spring-boot-oauth2/ for reference about configuring OAuth2 login // also http://cscarioni.blogspot.ch/2013/04/pro-spring-security-and-oauth-2.html @@ -84,186 +64,178 @@ import javax.net.ssl.X509TrustManager; @EnableOAuth2Client public class SecurityConfiguration extends WebSecurityConfigurerAdapter { - 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; - - /** - * Absolute URL to redirect to when login is required - */ - @Value("#{'${frontend.loginUrl:/login/hbp}'}") - 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}'}") - private String frontendRedirectAfterLogin; - - /** - * Absolute URL to redirect to after logout has occurred - */ - @Value("#{'${frontend.redirectAfterLogoutUrl:/login/hbp}'}") - private String redirectAfterLogoutUrl; - - /** - * URL to revoke auth token - */ - @Value("#{'${hbp.resource.revokeTokenUri:https://services.humanbrainproject.eu/oidc/revoke}'}") - private String revokeTokenURI; - - - -// @Autowired -// private HttpServletRequest request; - - @Override - protected void configure(HttpSecurity http) throws Exception { - disableCertificateValidation(); - // @formatter:off - http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class); - - if (authentication) { - http.antMatcher("/**") - .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().authenticated() - .anyRequest().hasRole("Researcher") - .and().exceptionHandling().authenticationEntryPoint(new CustomLoginUrlAuthenticationEntryPoint(loginUrl)) - .and().logout().addLogoutHandler(authLogoutHandler()).logoutSuccessUrl(redirectAfterLogoutUrl) - .and().logout().permitAll() - .and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository()) - .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class) - .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class); - } - else { + 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; + + /** + * Absolute URL to redirect to when login is required + */ + @Value("#{'${frontend.loginUrl:/login/hbp}'}") + 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}'}") + private String frontendRedirectAfterLogin; + + /** + * Absolute URL to redirect to after logout has occurred + */ + @Value("#{'${frontend.redirectAfterLogoutUrl:/login/hbp}'}") + private String redirectAfterLogoutUrl; + + /** + * URL to revoke auth token + */ + @Value("#{'${hbp.resource.revokeTokenUri:https://services.humanbrainproject.eu/oidc/revoke}'}") + private String revokeTokenURI; + + @Override + protected void configure(HttpSecurity http) throws Exception { + disableCertificateValidation(); + // @formatter:off + http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class); + + if (authentication) { + http.antMatcher("/**") + .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().authenticated() + .anyRequest().hasRole("Researcher") + .and().exceptionHandling().authenticationEntryPoint(new CustomLoginUrlAuthenticationEntryPoint(loginUrl)) + .accessDeniedHandler(new CustomAccessDeniedHandler()) + .and().logout().addLogoutHandler(authLogoutHandler()).logoutSuccessUrl(redirectAfterLogoutUrl) + .and().logout().permitAll() + .and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository()) + .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class) + .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class); + } else { http.antMatcher("/**") .authorizeRequests() .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(); - } - - @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() { - @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; - } - - 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); - } - - } - } - + } + } + + 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(); + } + + public boolean isAuthentication() { + return authentication; + } + + public String getFrontendRedirectAfterLogin() { + return frontendRedirectAfterLogin; + } + + 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; + } + + 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(); @@ -291,68 +263,69 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { return String.join(",", authorities); } } - - - private LogoutHandler authLogoutHandler() { - return (request, response, authentication) -> { - logout(); - }; + + + private LogoutHandler authLogoutHandler() { + return (request, response, authentication) -> { + logout(); + }; } - - - public void logout() { - // POSTã™ã‚‹ãƒªã‚¯ã‚¨ã‚¹ãƒˆãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã‚’ä½œæˆ - UserActionLogging.LogAction("refresh token ", this.oauth2ClientContext.getAccessToken().getRefreshToken().getValue()); - RestTemplate restTemplate = new RestTemplate(); - MultiValueMap<String, String> formParams = new LinkedMultiValueMap<>(); - formParams.add("client_id", hbp().getClientId()); + + + public void logout() { + // POSTã™ã‚‹ãƒªã‚¯ã‚¨ã‚¹ãƒˆãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã‚’ä½œæˆ + UserActionLogging.LogAction("refresh token ", this.oauth2ClientContext.getAccessToken().getRefreshToken().getValue()); + 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); - // ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’ä½œæˆ - UserActionLogging.LogAction("logoutUri is ", logoutUri); - RequestEntity<MultiValueMap<String, String>> requestEntity = - new RequestEntity<>(formParams, httpHeaders, HttpMethod.POST, - URI.create(logoutUri)); - // POSTリクエストé€ä¿¡ï¼ˆãƒã‚°ã‚¢ã‚¦ãƒˆå®Ÿè¡Œï¼‰ - - ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity, String.class); + formParams.add("refresh_token", this.oauth2ClientContext.getAccessToken().getRefreshToken().getValue()); + // ãƒªã‚¯ã‚¨ã‚¹ãƒˆãƒ˜ãƒƒãƒ€ãƒ¼ã‚’ä½œæˆ + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE); + // ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’ä½œæˆ + UserActionLogging.LogAction("logoutUri is ", logoutUri); + 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}'}") - private String keycloakUrl; - + + @Value("#{'${services.keycloak.keycloakUrl}'}") + private String keycloakUrl; + // static { - // disableCertificateValidation(); + // disableCertificateValidation(); // } public void disableCertificateValidation() { - LOGGER.info("disabling certificate validation host : " + keycloakUrl); + LOGGER.info("disabling certificate validation host : " + keycloakUrl); // Create a trust manager that does not validate certificate chains - TrustManager[] trustAllCerts = new TrustManager[] { + 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) {} - } }; + + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + }}; // 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)) - { + // + session.getPeerHost()); + if (hostname.equals(keycloakUrl) && session.getPeerHost().equals(keycloakUrl)) { return true; - } - else - { + } else { return false; } } @@ -364,8 +337,9 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { sc.init(null, trustAllCerts, new SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier(hv); - } catch (Exception e) {} + } catch (Exception e) { + } - } + } } diff --git a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java index efe69c07207cd1b247b20cc9a953a37cb8bea7b5..6b92c691a633ecbd4c16ccea813fc8b3c3533742 100644 --- a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java +++ b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java @@ -8,11 +8,13 @@ import com.google.gson.Gson; 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.UserInfo; import eu.hbp.mip.model.galaxy.WorkflowDTO; import eu.hbp.mip.utils.HTTPUtil; import eu.hbp.mip.utils.UserActionLogging; 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.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; @@ -35,6 +37,9 @@ public class AlgorithmsApi { private static final Gson gson = new Gson(); + @Autowired + private UserInfo userInfo; + @Value("#{'${services.exareme.algorithmsUrl}'}") private String exaremeAlgorithmsUrl; @@ -47,7 +52,7 @@ public class AlgorithmsApi { @ApiOperation(value = "List all algorithms", response = String.class) @RequestMapping(method = RequestMethod.GET) public ResponseEntity<List<AlgorithmDTO>> getAlgorithms() { - UserActionLogging.LogAction("List all algorithms", ""); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "List all algorithms", ""); List<AlgorithmDTO> exaremeAlgorithms = getExaremeAlgorithms(); List<AlgorithmDTO> galaxyAlgorithms = getGalaxyWorkflows(); @@ -56,13 +61,13 @@ public class AlgorithmsApi { if (exaremeAlgorithms != null) { algorithms.addAll(exaremeAlgorithms); } else { - UserActionLogging.LogAction("List all algorithms", + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "List all algorithms", "Getting exareme algorithms failed and returned null"); } if (galaxyAlgorithms != null) { algorithms.addAll(galaxyAlgorithms); } else { - UserActionLogging.LogAction("List all algorithms", + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "List all algorithms", "Getting galaxy workflows failed and returned null"); } @@ -75,7 +80,7 @@ public class AlgorithmsApi { * @return a list of AlgorithmDTOs or null if something fails */ public List<AlgorithmDTO> getExaremeAlgorithms() { - UserActionLogging.LogAction("List exareme algorithms", ""); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "List exareme algorithms", ""); List<AlgorithmDTO> algorithms = new LinkedList<>(); // Get exareme algorithms @@ -85,11 +90,11 @@ public class AlgorithmsApi { algorithms = gson.fromJson(response.toString(), algorithms.getClass()); } catch (IOException e) { - UserActionLogging.LogAction("List exareme algorithms", "An exception occurred: " + e.getMessage()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "List exareme algorithms", "An exception occurred: " + e.getMessage()); return null; } - UserActionLogging.LogAction("List exareme algorithms", + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "List exareme algorithms", "Completed, returned " + algorithms.size() + " algorithms."); return algorithms; } @@ -100,7 +105,7 @@ public class AlgorithmsApi { * @return a list of AlgorithmDTOs or null if something fails */ public List<AlgorithmDTO> getGalaxyWorkflows() { - UserActionLogging.LogAction("List Galaxy workflows", ""); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "List Galaxy workflows", ""); List<Workflow> workflowList = null; try { @@ -110,7 +115,7 @@ public class AlgorithmsApi { workflowList = new ArrayList<>(workflowsClient.getWorkflows()); } catch (Exception e) { - UserActionLogging.LogAction("List Galaxy workflows", "Error when calling list galaxy workflows: " + e.getMessage()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "List Galaxy workflows", "Error when calling list galaxy workflows: " + e.getMessage()); return null; } @@ -130,28 +135,28 @@ public class AlgorithmsApi { } else { // Something unexpected happened String msgErr = gson.toJson(response.errorBody()); - UserActionLogging.LogAction("List Galaxy workflows", "Error Response: " + msgErr); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "List Galaxy workflows", "Error Response: " + msgErr); return null; } } catch (Exception e) { - UserActionLogging.LogAction("List Galaxy workflows", "An exception occurred: " + e.getMessage()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "List Galaxy workflows", "An exception occurred: " + e.getMessage()); return null; } } - UserActionLogging.LogAction("List Galaxy workflows", "Workflows fetched: " + workflows.size()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "List Galaxy workflows", "Workflows fetched: " + workflows.size()); // Convert the workflows to algorithms List<AlgorithmDTO> algorithms = new LinkedList<>(); for (WorkflowDTO workflow : workflows) { - UserActionLogging.LogAction("List Galaxy workflows", "Converting workflow: " + workflow); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "List Galaxy workflows", "Converting workflow: " + workflow); algorithms.add(workflow.convertToAlgorithmDTO()); - UserActionLogging.LogAction("List Galaxy workflows", + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "List Galaxy workflows", "Converted algorithm: " + algorithms.get(algorithms.size() - 1)); } - UserActionLogging.LogAction("List Galaxy workflows", "Completed!"); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "List Galaxy workflows", "Completed!"); return algorithms; } } diff --git a/src/main/java/eu/hbp/mip/controllers/ArticlesApi.java b/src/main/java/eu/hbp/mip/controllers/ArticlesApi.java index 963b181576f0626620d76b3a0370dcb840767778..2e5fb3dc551a96cf0fd34b89cd4d159d7fcbb67f 100644 --- a/src/main/java/eu/hbp/mip/controllers/ArticlesApi.java +++ b/src/main/java/eu/hbp/mip/controllers/ArticlesApi.java @@ -66,7 +66,7 @@ public class ArticlesApi { } } } - UserActionLogging.LogAction("Get articles", "id : Get All articles"); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Get articles", "id : Get All articles"); return ResponseEntity.ok(articles); } @@ -127,7 +127,7 @@ public class ArticlesApi { } articleRepository.save(article); - UserActionLogging.LogAction("Created article", "id : " + article.getSlug()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Created article", "id : " + article.getSlug()); return new ResponseEntity<>(HttpStatus.CREATED); } @@ -137,7 +137,7 @@ public class ArticlesApi { public ResponseEntity<Article> getAnArticle( @ApiParam(value = "slug", required = true) @PathVariable("slug") String slug ) { - UserActionLogging.LogAction("Getting an article", "id : " + slug); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Getting an article", "id : " + slug); User user = userInfo.getUser(); Article article; @@ -165,7 +165,7 @@ public class ArticlesApi { @ApiParam(value = "slug", required = true) @PathVariable("slug") String slug, @RequestBody @ApiParam(value = "Article to update", required = true) @Valid Article article ) { - UserActionLogging.LogAction("Update an article", "id : " + slug); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Update an article", "id : " + slug); User user = userInfo.getUser(); diff --git a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java index f876e315ed224bfc667b6ff6fb9337a2bafb361f..3958d99d608fff64bfcf934ee22e5ef61a44e303 100644 --- a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java +++ b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java @@ -17,6 +17,7 @@ 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.UserActionLogging; import io.swagger.annotations.Api; @@ -28,6 +29,7 @@ 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; @@ -50,6 +52,9 @@ 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(); @@ -68,8 +73,9 @@ public class ExperimentApi { @Value("#{'${services.galaxy.galaxyApiKey}'}") private String galaxyApiKey; - @Autowired - private UserInfo userInfo; + // Enable HBP collab authentication (1) or disable it (0). Default is 1 + @Value("#{'${hbp.authentication.enabled:1}'}") + private boolean authenticationIsEnabled; @Autowired private ModelRepository modelRepository; @@ -87,7 +93,7 @@ public class ExperimentApi { try { experimentUuid = UUID.fromString(uuid); } catch (IllegalArgumentException iae) { - UserActionLogging.LogAction("Get Experiment", "Invalid Experiment UUID."); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Get Experiment", "Invalid Experiment UUID."); return ResponseEntity.badRequest().body("Invalid Experiment UUID"); } @@ -97,19 +103,60 @@ public class ExperimentApi { return new ResponseEntity<>("Not found", HttpStatus.NOT_FOUND); } - UserActionLogging.LogAction("Get an experiment ", " uuid : " + uuid); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Get an experiment ", " 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(@RequestBody ExperimentExecutionDTO experimentExecutionDTO) { - UserActionLogging.LogAction("Run algorithm", "Running the algorithm..."); + public ResponseEntity<String> runExperiment(Authentication authentication, @RequestBody ExperimentExecutionDTO experimentExecutionDTO) { + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run algorithm", "Running the algorithm..."); + + if(authenticationIsEnabled) { + // --- Validating proper access rights on the datasets --- + List<String> userClaims = Arrays.asList(authentication.getAuthorities().toString().toLowerCase() + .replaceAll("[\\s+\\]\\[]", "").split(",")); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "User Claims", userClaims.toString()); + + // Don't check for dataset claims if "super" claim exists allowing everything + if (!userClaims.contains(ClaimUtils.allDatasetsAllowedClaim())) { + // Getting the dataset from the experiment parameters + String experimentDatasets = null; + for (AlgorithmExecutionParamDTO parameter : experimentExecutionDTO.getAlgorithms().get(0).getParameters()) { + if (parameter.getName().equals("dataset")) { + experimentDatasets = parameter.getValue(); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run algorithm", "Found the dataset parameter!"); + break; + } + } + + if (experimentDatasets == null || experimentDatasets.equals("")) { + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run algorithm", + "A dataset should be specified when running an algorithm."); + return ResponseEntity.badRequest().body("A dataset should be specified when running an algorithm."); + } + + for (String dataset : experimentDatasets.split(",")) { + String datasetRole = ClaimUtils.getDatasetClaim(dataset); + if (!userClaims.contains(datasetRole.toLowerCase())) { + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run algorithm", + "You are not allowed to use dataset: " + dataset); + return ResponseEntity.status(HttpStatus.FORBIDDEN).body("You are not allowed to use dataset: " + dataset); + } + } + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run algorithm", + "User is authorized to use the datasets: " + experimentDatasets); + } + } + + // --- Run the experiment --- // Get the type of algorithm String algorithmType = experimentExecutionDTO.getAlgorithms().get(0).getType(); + // Run with the appropriate engine if (algorithmType.equals("workflow")) { return runGalaxyWorkflow(experimentExecutionDTO); } else { @@ -122,7 +169,7 @@ public class ExperimentApi { public ResponseEntity<String> markExperimentAsViewed( @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - UserActionLogging.LogAction("Mark an experiment as viewed", " uuid : " + uuid); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Mark an experiment as viewed", " uuid : " + uuid); Experiment experiment; UUID experimentUuid; @@ -130,8 +177,7 @@ public class ExperimentApi { try { experimentUuid = UUID.fromString(uuid); } catch (IllegalArgumentException iae) { - //LOGGER.trace("Invalid UUID", iae); - //LOGGER.warn("An invalid Experiment UUID was received !"); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Mark an experiment as viewed", "Invalid Experiment UUID" + uuid); return ResponseEntity.badRequest().body("Invalid Experiment UUID"); } @@ -141,7 +187,7 @@ public class ExperimentApi { experiment.setResultsViewed(true); experimentRepository.save(experiment); - UserActionLogging.LogAction("Experiment updated (marked as viewed)", " "); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Experiment updated (marked as viewed)", " "); return new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); } @@ -151,7 +197,7 @@ public class ExperimentApi { public ResponseEntity<String> markExperimentAsShared( @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - UserActionLogging.LogAction("Mark an experiment as shared", " uuid : " + uuid); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Mark an experiment as shared", " uuid : " + uuid); return doMarkExperimentAsShared(uuid, true); } @@ -160,7 +206,7 @@ public class ExperimentApi { @RequestMapping(value = "/{uuid}/markAsUnshared", method = RequestMethod.GET) public ResponseEntity<String> markExperimentAsUnshared( @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - UserActionLogging.LogAction("Mark an experiment as unshared", " uuid : " + uuid); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Mark an experiment as unshared", " uuid : " + uuid); return doMarkExperimentAsShared(uuid, false); } @@ -170,7 +216,7 @@ public class ExperimentApi { public ResponseEntity<String> listExperiments( @ApiParam(value = "maxResultCount") @RequestParam int maxResultCount) { - UserActionLogging.LogAction("List experiments", " maxResultCount : " + maxResultCount); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "List experiments", " maxResultCount : " + maxResultCount); return doListExperiments(false, null); } @@ -180,7 +226,7 @@ public class ExperimentApi { public ResponseEntity<String> listExperiments(@ApiParam(value = "slug") @RequestParam("slug") String modelSlug, @ApiParam(value = "maxResultCount") @RequestParam("maxResultCount") int maxResultCount) { - UserActionLogging.LogAction("List experiments", " modelSlug : " + modelSlug); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "List experiments", " modelSlug : " + modelSlug); if (maxResultCount <= 0 && (modelSlug == null || "".equals(modelSlug))) { return new ResponseEntity<>("You must provide at least a slug or a limit of result", @@ -192,8 +238,8 @@ public class ExperimentApi { @ApiOperation(value = "list my experiments", response = Experiment.class, responseContainer = "List") @RequestMapping(method = RequestMethod.GET, params = {"mine"}) - public ResponseEntity<String> listMyExperiments(@ApiParam(value = "mine") @RequestParam("mine") boolean mine) { - UserActionLogging.LogAction("List my experiments", " mine : " + mine); + public ResponseEntity<String> listMyExperiments(Authentication authentication, @ApiParam(value = "mine") @RequestParam("mine") boolean mine) { + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "List my experiments", " mine : " + mine); return doListExperiments(true, null); } @@ -244,14 +290,14 @@ public class ExperimentApi { experiment.setShared(shared); experimentRepository.save(experiment); - UserActionLogging.LogAction("Experiment updated (marked as shared)", ""); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Experiment updated (marked as shared)", ""); return new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); } /* ------------------------------- EXPERIMENT MODEL METHODS ----------------------------------------------------*/ - public Experiment createExperiment(ExperimentExecutionDTO experimentExecutionDTO){ + public Experiment createExperiment(ExperimentExecutionDTO experimentExecutionDTO) { User user = userInfo.getUser(); Experiment experiment = new Experiment(); @@ -263,31 +309,31 @@ public class ExperimentApi { experiment.setName(experimentExecutionDTO.getName()); experimentRepository.save(experiment); - UserActionLogging.LogAction("Created an experiment", " id : " + experiment.getUuid()); - UserActionLogging.LogAction("Created an experiment", " algorithms : " + experiment.getAlgorithms()); - UserActionLogging.LogAction("Created an experiment", " model : " + experiment.getModel().getSlug()); - UserActionLogging.LogAction("Created an experiment", " name : " + experiment.getName()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Created an experiment", " id : " + experiment.getUuid()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Created an experiment", " algorithms : " + experiment.getAlgorithms()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Created an experiment", " model : " + experiment.getModel().getSlug()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Created an experiment", " name : " + experiment.getName()); return experiment; } private void saveExperiment(Experiment experiment) { - UserActionLogging.LogAction("Saved an experiment", " id : " + experiment.getUuid()); - UserActionLogging.LogAction("Saved an experiment", " algorithms : " + experiment.getAlgorithms()); - UserActionLogging.LogAction("Saved an experiment", " model : " + experiment.getModel().getSlug()); - UserActionLogging.LogAction("Saved an experiment", " name : " + experiment.getName()); - UserActionLogging.LogAction("Saved an experiment", " historyId : " + experiment.getWorkflowHistoryId()); - UserActionLogging.LogAction("Saved an experiment", " status : " + experiment.getWorkflowStatus()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Saved an experiment", " id : " + experiment.getUuid()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Saved an experiment", " algorithms : " + experiment.getAlgorithms()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Saved an experiment", " model : " + experiment.getModel().getSlug()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Saved an experiment", " name : " + experiment.getName()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Saved an experiment", " historyId : " + experiment.getWorkflowHistoryId()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Saved an experiment", " status : " + experiment.getWorkflowStatus()); experimentRepository.save(experiment); - UserActionLogging.LogAction("Experiment saved", ""); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Experiment saved", ""); } private void finishExperiment(Experiment experiment) { experiment.setFinished(new Date()); experimentRepository.save(experiment); - UserActionLogging.LogThreadAction("Experiment finished!", ""); + UserActionLogging.LogAction("Experiment finished!", ""); } /* -------------------------------------- EXAREME CALLS ---------------------------------------------------------*/ @@ -299,7 +345,7 @@ public class ExperimentApi { * @return the response to be returned */ public ResponseEntity<String> runExaremeAlgorithm(ExperimentExecutionDTO experimentExecutionDTO) { - UserActionLogging.LogAction("Run exareme algorithm", "Running the algorithm..."); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run exareme algorithm", "Running the algorithm..."); Experiment experiment = createExperiment(experimentExecutionDTO); @@ -312,26 +358,26 @@ public class ExperimentApi { String body = gson.toJson(algorithmParameters); String url = queryExaremeUrl + "/" + algorithmName; - UserActionLogging.LogAction("Run exareme algorithm", "url: " + url + ", body: " + body); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run exareme algorithm", "url: " + url + ", body: " + body); ResponseEntity<String> response = new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); - UserActionLogging.LogAction("Run exareme algorithm", + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run exareme algorithm", "Completed, returning: " + experiment.toString()); - UserActionLogging.LogAction("Run exareme algorithm", + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run exareme algorithm", "Starting exareme execution thread"); new Thread(() -> { - // ATTENTION: Inside the Thread only LogThreadAction should be used, not LogAction! - UserActionLogging.LogThreadAction("Run exareme algorithm", + // ATTENTION: Inside the Thread only LogAction should be used, not LogAction! + UserActionLogging.LogAction("Run exareme algorithm", "Thread started!"); try { - UserActionLogging.LogThreadAction("Run exareme algorithm", + UserActionLogging.LogAction("Run exareme algorithm", "Thread started!"); StringBuilder results = new StringBuilder(); int code = HTTPUtil.sendPost(url, body, results); - UserActionLogging.LogThreadAction("Run exareme algorithm", + UserActionLogging.LogAction("Run exareme algorithm", "Algorithm finished with code: " + code); // Results are stored in the experiment object @@ -339,18 +385,18 @@ public class ExperimentApi { experiment.setHasError(code >= 400); experiment.setHasServerError(code >= 500); } catch (Exception e) { - UserActionLogging.LogThreadAction("Run exareme algorithm", + UserActionLogging.LogAction("Run exareme algorithm", "There was an exception: " + e.getMessage()); experiment.setHasError(true); experiment.setHasServerError(true); experiment.setResult(e.getMessage()); } - UserActionLogging.LogThreadAction("Run exareme algorithm", + UserActionLogging.LogAction("Run exareme algorithm", "Finished the experiment: " + experiment.toString()); finishExperiment(experiment); - UserActionLogging.LogThreadAction("Run exareme algorithm", + UserActionLogging.LogAction("Run exareme algorithm", "Finished!"); }).start(); @@ -367,7 +413,7 @@ public class ExperimentApi { * @return the response to be returned */ public ResponseEntity<String> runGalaxyWorkflow(ExperimentExecutionDTO experimentExecutionDTO) { - UserActionLogging.LogAction("Run workflow", "Running a workflow..."); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run workflow", "Running a workflow..."); Experiment experiment = createExperiment(experimentExecutionDTO); @@ -397,7 +443,7 @@ public class ExperimentApi { } } if (workflow == null) { - UserActionLogging.LogAction("Run workflow", + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run workflow", "Could not find algorithm code: " + workflowId); return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(new ErrorResponse("Could not find galaxy algorithm.").toString()); @@ -416,7 +462,7 @@ public class ExperimentApi { // Create the request client RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); - UserActionLogging.LogAction("Run workflow", "Running Galaxy workflow with id: " + workflow.getId()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run workflow", "Running Galaxy workflow with id: " + workflow.getId()); // Call Galaxy to run the workflow Call<PostWorkflowToGalaxyDtoResponse> call = service.postWorkflowToGalaxy(workflow.getId(), galaxyApiKey, requestBodyJson); @@ -425,7 +471,7 @@ public class ExperimentApi { if (response.code() == 200) { // Call succeeded String responseBody = gson.toJson(response.body()); - UserActionLogging.LogAction("Run workflow", "Response: " + responseBody); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run workflow", "Response: " + responseBody); String historyId = (String) new JSONObject(responseBody).get("history_id"); experiment.setWorkflowHistoryId(historyId); @@ -435,7 +481,7 @@ public class ExperimentApi { } else { // Something unexpected happened String msgErr = gson.toJson(response.errorBody()); - UserActionLogging.LogAction("Run workflow", "Error Response: " + msgErr); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run workflow", "Error Response: " + msgErr); // Values are read from streams. JSONObject jObjectError = new JSONObject(msgErr); @@ -447,7 +493,7 @@ public class ExperimentApi { } } catch (Exception e) { - UserActionLogging.LogAction("Run workflow", "An exception occurred: " + e.getMessage()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run workflow", "An exception occurred: " + e.getMessage()); experiment.setHasError(true); experiment.setHasServerError(true); experiment.setResult(e.getMessage()); @@ -457,75 +503,75 @@ public class ExperimentApi { // Start the process of fetching the status updateWorkflowExperiment(experiment); - UserActionLogging.LogAction("Run workflow", "Run workflow completed!"); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run workflow", "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 + * 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 + * @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) { if (experiment == null) { - UserActionLogging.LogAction("Update workflow experiment", "The experiment does not exist."); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Update workflow experiment", "The experiment does not exist."); return; } - UserActionLogging.LogAction("Update workflow experiment", + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Update workflow experiment", " Experiment id : " + experiment.getUuid()); if (experiment.getWorkflowHistoryId() == null) { - UserActionLogging.LogAction("Update workflow experiment", "History Id does not exist."); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Update workflow experiment", "History Id does not exist."); return; } - UserActionLogging.LogAction("Update workflow experiment", "Starting Thread..." ); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Update workflow experiment", "Starting Thread..."); new Thread(() -> { - while(true) { - // ATTENTION: Inside the Thread only LogThreadAction should be used, not LogAction! - UserActionLogging.LogThreadAction("Update workflow experiment", "Thread is running..."); + while (true) { + // ATTENTION: Inside the Thread only LogAction should be used, not LogAction! + UserActionLogging.LogAction("Update workflow experiment", "Thread is running..."); try { sleep(2000); } catch (InterruptedException e) { - UserActionLogging.LogThreadAction("Update workflow experiment", + UserActionLogging.LogAction("Update workflow experiment", "Sleep was disrupted: " + e.getMessage()); } - UserActionLogging.LogThreadAction("Update workflow experiment", + UserActionLogging.LogAction("Update workflow experiment", "Fetching status for experiment Id: " + experiment.getUuid()); String state = getWorkflowStatus(experiment.getWorkflowHistoryId()); - UserActionLogging.LogThreadAction("Update workflow experiment", "State is: " + state); + UserActionLogging.LogAction("Update workflow experiment", "State is: " + state); switch (state) { case "running": // Do nothing, when the experiment is created the status is set to running - UserActionLogging.LogThreadAction("Update workflow experiment", + UserActionLogging.LogAction("Update workflow experiment", "Workflow is still running."); break; case "completed": // Get only the job result that is visible List<GalaxyWorkflowResult> workflowJobsResults = getWorkflowResults(experiment.getWorkflowHistoryId()); - UserActionLogging.LogThreadAction("Update workflow experiment", + UserActionLogging.LogAction("Update workflow experiment", "Results are: " + workflowJobsResults.toString()); boolean resultFound = false; for (GalaxyWorkflowResult jobResult : workflowJobsResults) { if (jobResult.getVisible()) { - UserActionLogging.LogThreadAction("Update workflow experiment", + UserActionLogging.LogAction("Update workflow experiment", "Visible result are: " + jobResult.getId()); String result = getWorkflowResultBody(experiment.getWorkflowHistoryId(), jobResult.getId()); - UserActionLogging.LogThreadAction("Update workflow experiment", "Result: " + result); + UserActionLogging.LogAction("Update workflow experiment", "Result: " + result); if (result == null) { experiment.setHasError(true); experiment.setHasServerError(true); @@ -537,7 +583,7 @@ public class ExperimentApi { } if (!resultFound) { // If there is no visible result - UserActionLogging.LogThreadAction("Update workflow experiment", "No visible result"); + UserActionLogging.LogAction("Update workflow experiment", "No visible result"); experiment.setResult("[" + new ErrorResponse("The workflow has no visible result.").toString() + "]"); experiment.setHasError(true); experiment.setHasServerError(true); @@ -549,18 +595,18 @@ public class ExperimentApi { case "error": // Get the job result that failed workflowJobsResults = getWorkflowResults(experiment.getWorkflowHistoryId()); - UserActionLogging.LogThreadAction("Update workflow experiment", + UserActionLogging.LogAction("Update workflow experiment", "Error results are: " + workflowJobsResults.toString()); boolean failedJobFound = false; for (GalaxyWorkflowResult jobResult : workflowJobsResults) { if (jobResult.getState().equals("error")) { - UserActionLogging.LogThreadAction("Update workflow experiment", + UserActionLogging.LogAction("Update workflow experiment", "Failed job is: " + jobResult.getId()); String result = getWorkflowJobError(jobResult.getId()); - UserActionLogging.LogThreadAction("Update workflow experiment", "Job result: " + result); + UserActionLogging.LogAction("Update workflow experiment", "Job result: " + result); if (result == null) { experiment.setHasError(true); experiment.setHasServerError(true); @@ -572,7 +618,7 @@ public class ExperimentApi { } if (!failedJobFound) { // If there is no visible failed job - UserActionLogging.LogThreadAction("Update workflow experiment", "No failed result"); + UserActionLogging.LogAction("Update workflow experiment", "No failed result"); experiment.setResult("[" + new ErrorResponse("The workflow has no failed result.").toString() + "]"); experiment.setHasError(true); experiment.setHasServerError(true); @@ -590,7 +636,7 @@ public class ExperimentApi { // If result exists return if (experiment.getResult() != null) { - UserActionLogging.LogThreadAction("Update workflow experiment", + UserActionLogging.LogAction("Update workflow experiment", "Result exists: " + experiment.getResult()); return; } @@ -607,8 +653,8 @@ public class ExperimentApi { * "completed" -> When the workflow completed successfully */ public String getWorkflowStatus(String historyId) { - // ATTENTION: This function is used from a Thread. Only LogThreadAction should be used, not LogAction! - UserActionLogging.LogThreadAction("Get workflow status", " History Id : " + historyId); + // ATTENTION: This function is used from a Thread. Only LogAction should be used, not LogAction! + UserActionLogging.LogAction("Get workflow status", " History Id : " + historyId); // Create the request client RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); @@ -618,15 +664,15 @@ public class ExperimentApi { try { Response<Object> response = call.execute(); if (response.code() >= 400) { - UserActionLogging.LogThreadAction("Get workflow status", " Response code: " + UserActionLogging.LogAction("Get workflow status", " Response code: " + response.code() + "" + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); return "internalError"; } result = new Gson().toJson(response.body()); - UserActionLogging.LogThreadAction("Get workflow status", " Result: " + result); + UserActionLogging.LogAction("Get workflow status", " Result: " + result); } catch (IOException e) { - UserActionLogging.LogThreadAction("Get workflow status" + UserActionLogging.LogAction("Get workflow status" , " An exception happened: " + e.getMessage()); return "internalError"; } @@ -636,12 +682,12 @@ public class ExperimentApi { JSONObject resultJson = new JSONObject(result); state = resultJson.getString("state"); } catch (JSONException e) { - UserActionLogging.LogThreadAction("Get workflow status" + UserActionLogging.LogAction("Get workflow status" , " An exception happened: " + e.getMessage()); return "internalError"; } - UserActionLogging.LogThreadAction("Get workflow status", " Completed!"); + UserActionLogging.LogAction("Get workflow status", " Completed!"); switch (state) { case "ok": return "completed"; @@ -662,7 +708,7 @@ public class ExperimentApi { * @return a List<GalaxyWorkflowResult> or null when an error occurred */ public List<GalaxyWorkflowResult> getWorkflowResults(String historyId) { - UserActionLogging.LogThreadAction("Get workflow results", " historyId : " + historyId); + UserActionLogging.LogAction("Get workflow results", " historyId : " + historyId); RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); Call<List<GalaxyWorkflowResult>> call = service.getWorkflowResultsFromGalaxy(historyId, galaxyApiKey); @@ -671,20 +717,20 @@ public class ExperimentApi { try { Response<List<GalaxyWorkflowResult>> response = call.execute(); if (response.code() >= 400) { - UserActionLogging.LogThreadAction("Get workflow results", " Response code: " + UserActionLogging.LogAction("Get workflow results", " Response code: " + response.code() + "" + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); return null; } getGalaxyWorkflowResultList = response.body(); - UserActionLogging.LogThreadAction("Get workflow results", " Result: " + response.body()); + UserActionLogging.LogAction("Get workflow results", " Result: " + response.body()); } catch (IOException e) { - UserActionLogging.LogThreadAction("Get workflow results" + UserActionLogging.LogAction("Get workflow results" , " An exception happened: " + e.getMessage()); return null; } - UserActionLogging.LogThreadAction("Get workflow results", " Completed!"); + UserActionLogging.LogAction("Get workflow results", " Completed!"); return getGalaxyWorkflowResultList; } @@ -695,7 +741,7 @@ public class ExperimentApi { * @return the result of the specific workflow job, null if there was an error */ public String getWorkflowResultBody(String historyId, String contentId) { - UserActionLogging.LogThreadAction("Get workflow results Body", " historyId : " + historyId); + UserActionLogging.LogAction("Get workflow results Body", " historyId : " + historyId); RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); Call<Object> call = @@ -705,20 +751,20 @@ public class ExperimentApi { try { Response<Object> response = call.execute(); if (response.code() >= 400) { - UserActionLogging.LogThreadAction("Get workflow results Body", " Response code: " + UserActionLogging.LogAction("Get workflow results Body", " Response code: " + response.code() + "" + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); return null; } resultJson = new Gson().toJson(response.body()); - UserActionLogging.LogThreadAction("Get workflow results Body", " Result: " + resultJson); + UserActionLogging.LogAction("Get workflow results Body", " Result: " + resultJson); } catch (IOException e) { - UserActionLogging.LogThreadAction("Get workflow results Body", + UserActionLogging.LogAction("Get workflow results Body", " An exception happened: " + e.getMessage()); return null; } - UserActionLogging.LogThreadAction("Get workflow results Body", " Completed!"); + UserActionLogging.LogAction("Get workflow results Body", " Completed!"); return resultJson; } @@ -728,7 +774,7 @@ public class ExperimentApi { * @return the error that was produced or null if an error occurred */ public String getWorkflowJobError(String jobId) { - UserActionLogging.LogThreadAction("Get workflow job error", " jobId : " + jobId); + UserActionLogging.LogAction("Get workflow job error", " jobId : " + jobId); RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); Call<Object> callError = service.getErrorMessageOfWorkflowFromGalaxy(jobId, galaxyApiKey); @@ -738,7 +784,7 @@ public class ExperimentApi { try { Response<Object> response = callError.execute(); if (response.code() >= 400) { - UserActionLogging.LogThreadAction("Get workflow job error", "Response code: " + UserActionLogging.LogAction("Get workflow job error", "Response code: " + response.code() + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); return null; } @@ -748,19 +794,19 @@ public class ExperimentApi { JsonElement jsonElement = new JsonParser().parse(jsonString); JsonObject rootObject = jsonElement.getAsJsonObject(); fullError = rootObject.get("stderr").getAsString(); - UserActionLogging.LogThreadAction("Get workflow job error", "Error: " + fullError); + UserActionLogging.LogAction("Get workflow job error", "Error: " + fullError); String[] arrOfStr = fullError.split("ValueError", 0); String specError = arrOfStr[arrOfStr.length - 1]; returnError = specError.substring(1); - UserActionLogging.LogThreadAction("Get workflow job error", "Parsed Error: " + returnError); + UserActionLogging.LogAction("Get workflow job error", "Parsed Error: " + returnError); } catch (IOException e) { - UserActionLogging.LogThreadAction("Get workflow job error", "Exception: " + e.getMessage()); + UserActionLogging.LogAction("Get workflow job error", "Exception: " + e.getMessage()); return null; } - UserActionLogging.LogThreadAction("Get workflow job error", "Completed successfully!"); + UserActionLogging.LogAction("Get workflow job error", "Completed successfully!"); return returnError; } diff --git a/src/main/java/eu/hbp/mip/controllers/FilesAPI.java b/src/main/java/eu/hbp/mip/controllers/FilesAPI.java index 5acdb38ca88a851cb96a3c375c51165b1e157531..3b4ec96ab88749ea8cfb64777c8bd79f090fa12d 100644 --- a/src/main/java/eu/hbp/mip/controllers/FilesAPI.java +++ b/src/main/java/eu/hbp/mip/controllers/FilesAPI.java @@ -36,12 +36,12 @@ public class FilesAPI { public ResponseEntity<Void> getProtectedFile( @ApiParam(value = "filename", required = true) @PathVariable("filename") String filename ) { - UserActionLogging.LogAction("Get protected file", " filename : " + filename); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Get protected file", " filename : " + filename); String filepath = "/protected/" + filename; String user = userInfo.getUser().getUsername(); String time = LocalDateTime.now().toString(); - UserActionLogging.LogAction("User " + user + " downloaded " + filepath, ""); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Downloaded " + filepath, ""); HttpHeaders headers = new HttpHeaders(); headers.add("X-Accel-Redirect", filepath); diff --git a/src/main/java/eu/hbp/mip/controllers/MiningApi.java b/src/main/java/eu/hbp/mip/controllers/MiningApi.java index 3adcc6269e8120147c6c81367c91b928c1d5d486..fba6ecf93718de3a4a4eea7b34af56e81e8e23a0 100644 --- a/src/main/java/eu/hbp/mip/controllers/MiningApi.java +++ b/src/main/java/eu/hbp/mip/controllers/MiningApi.java @@ -51,7 +51,7 @@ public class MiningApi { @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) { - UserActionLogging.LogAction("Run an histogram", ""); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run an histogram", ""); String query = gson.toJson(queryList); String url = queryExaremeUrl + "/" + "MULTIPLE_HISTOGRAMS"; @@ -69,7 +69,7 @@ public class MiningApi { @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) { - UserActionLogging.LogAction("Run descriptive stats", ""); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Run descriptive stats", ""); String query = gson.toJson(queryList); String url = queryExaremeUrl + "/" + "DESCRIPTIVE_STATS"; @@ -87,7 +87,7 @@ public class MiningApi { @ApiOperation(value = "Check if a formula is valid", response = String.class) @RequestMapping(value = "/checkFormula", method = RequestMethod.POST) public ResponseEntity checkFormulaValidity(String formula) { - UserActionLogging.LogAction("Check Formula Validity", ""); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Check Formula Validity", ""); 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 index 02ee9ac6b25b353876566a7261a7e6deba962574..633c01783d340a9b76bd71723fc62dd16db16fb1 100644 --- a/src/main/java/eu/hbp/mip/controllers/ModelsApi.java +++ b/src/main/java/eu/hbp/mip/controllers/ModelsApi.java @@ -54,7 +54,7 @@ public class ModelsApi { @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 ) { - UserActionLogging.LogAction("Get models",""); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Get models",""); User user = userInfo.getUser(); @@ -98,7 +98,7 @@ public class ModelsApi { @RequestBody @ApiParam(value = "Model to create", required = true) Model model ) { - UserActionLogging.LogAction("Create a model",""); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Create a model",""); User user = userInfo.getUser(); @@ -129,7 +129,7 @@ public class ModelsApi { } modelRepository.save(model); - UserActionLogging.LogAction("Model saved (also saved model.config and model.query)"," id : " + model.getSlug()); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Model saved (also saved model.config and model.query)"," id : " + model.getSlug()); return ResponseEntity.status(HttpStatus.CREATED).body(model); } @@ -192,7 +192,7 @@ public class ModelsApi { public ResponseEntity<Model> getAModel( @ApiParam(value = "slug", required = true) @PathVariable("slug") String slug ) { - UserActionLogging.LogAction("Get a model", " id : " + slug); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Get a model", " id : " + slug); User user = userInfo.getUser(); @@ -224,7 +224,7 @@ public class ModelsApi { @ApiParam(value = "slug", required = true) @PathVariable("slug") String slug, @RequestBody @ApiParam(value = "Model to update", required = true) Model model ) { - UserActionLogging.LogAction("Update a model", " id : "+ slug); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Update a model", " id : "+ slug); User user = userInfo.getUser(); Model oldModel = modelRepository.findOne(slug); @@ -269,7 +269,7 @@ public class ModelsApi { datasetRepository.save(model.getDataset()); modelRepository.save(model); - UserActionLogging.LogAction("Model updated (also saved/updated model.config and model.query)", " id : "+ slug); + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Model updated (also saved/updated model.config and model.query)", " id : "+ slug); 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 27f635d3278e9a2990489b4a3c01bd0d9851c34a..b7914e2215693126dfed7658b9bfa7e07796dec6 100644 --- a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java +++ b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java @@ -4,12 +4,20 @@ package eu.hbp.mip.controllers; -import com.fasterxml.jackson.core.type.TypeReference; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import eu.hbp.mip.model.PathologyDTO; +import eu.hbp.mip.model.PathologyDTO.PathologyDatasetDTO; +import eu.hbp.mip.model.UserInfo; +import eu.hbp.mip.utils.ClaimUtils; import eu.hbp.mip.utils.CustomResourceLoader; +import eu.hbp.mip.utils.UserActionLogging; 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.core.io.ResourceLoader; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @@ -18,7 +26,9 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import eu.hbp.mip.utils.UserActionLogging; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @@ -27,25 +37,78 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @Api(value = "/pathologies") public class PathologiesApi { - @RequestMapping(name = "/pathologies", method = RequestMethod.GET) - public String getPathologies() { - UserActionLogging.LogAction("load the pathologies", ""); - - return loadPathologies(); - } + private static final Gson gson = new Gson(); + + @Autowired + private UserInfo userInfo; + + // Enable HBP collab authentication (1) or disable it (0). Default is 1 + @Value("#{'${hbp.authentication.enabled:1}'}") + private boolean authenticationIsEnabled; @Autowired private CustomResourceLoader resourceLoader; - private String loadPathologies() { - Resource resource = resourceLoader.getResource("file:/opt/portal/api/pathologies.json"); - String result; + @RequestMapping(name = "/pathologies", method = RequestMethod.GET) + public ResponseEntity<String> getPathologies(Authentication authentication) { + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Load all the pathologies", ""); + + // Load pathologies from file + Resource resource = resourceLoader.getResource("file:/opt/portal/api/pathologies.json"); + List<PathologyDTO> allPathologies; try { - result = convertInputStreamToString(resource.getInputStream()); + allPathologies = gson.fromJson(convertInputStreamToString(resource.getInputStream()), new TypeToken<List<PathologyDTO>>() { + }.getType()); } catch (IOException e) { - result = "{\"error\" : \"The pathologies.json file could not be read.\"}"; + return ResponseEntity.badRequest().body("The pathologies.json file could not be read."); + } + + // If authentication is disabled return everything + if (!authenticationIsEnabled) { + return ResponseEntity.ok().body(gson.toJson(allPathologies)); } - return result; + + // --- Providing only the allowed pathologies/datasets to the user --- + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), + "Load all the pathologies", "Filter out the unauthorised datasets."); + + List<String> userClaims = Arrays.asList(authentication.getAuthorities().toString().toLowerCase() + .replaceAll("[\\s+\\]\\[]", "").split(",")); + + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), + "Load all the pathologies", "User Claims: " + userClaims); + + // If the "dataset_all" claim exists then return everything + if (userClaims.contains(ClaimUtils.allDatasetsAllowedClaim())) { + return ResponseEntity.ok().body(gson.toJson(allPathologies)); + } + + List<PathologyDTO> userPathologies = new ArrayList<>(); + for (PathologyDTO curPathology : allPathologies) { + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), + "Load all the pathologies", "Pathology: " + curPathology); + + List<PathologyDatasetDTO> userPathologyDatasets = new ArrayList<PathologyDatasetDTO>(); + for (PathologyDatasetDTO dataset : curPathology.getDatasets()) { + if (userClaims.contains(ClaimUtils.getDatasetClaim(dataset.getCode()))) { + userPathologyDatasets.add(dataset); + } + } + + if (userPathologyDatasets.size() > 0) { + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Load all the pathologies", + "Added pathology '" + curPathology.getLabel() + " with datasets: '" + userPathologyDatasets + "'"); + + PathologyDTO userPathology = new PathologyDTO(); + userPathology.setCode(curPathology.getCode()); + userPathology.setLabel(curPathology.getLabel()); + userPathology.setMetadataHierarchy(curPathology.getMetadataHierarchy()); + userPathology.setDatasets(userPathologyDatasets); + userPathologies.add(userPathology); + } + } + + return ResponseEntity.ok().body(gson.toJson(userPathologies)); } // Pure Java diff --git a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java index 0f93c437ea8477000e1881815cce984eca36f3f8..397f1ae6c149a3693632a0b7817709d6cb2318f3 100644 --- a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java +++ b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java @@ -5,20 +5,19 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.google.gson.JsonObject; import eu.hbp.mip.configuration.SecurityConfiguration; -import org.springframework.beans.factory.annotation.Value; import eu.hbp.mip.model.User; import eu.hbp.mip.model.UserInfo; import eu.hbp.mip.repositories.UserRepository; +import eu.hbp.mip.utils.UserActionLogging; import io.swagger.annotations.ApiParam; -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.condition.ConditionalOnExpression; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.prepost.PreAuthorize; -import eu.hbp.mip.utils.UserActionLogging; +import org.springframework.web.bind.annotation.*; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; @@ -45,8 +44,8 @@ public class SecurityApi { @RequestMapping(path = "/user", method = RequestMethod.GET) public Object user(Principal principal, HttpServletResponse response) { ObjectMapper mapper = new ObjectMapper(); - - UserActionLogging.LogAction("get user from /user",""); + + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "get user from /user", ""); try { String userJSON = mapper.writeValueAsString(userInfo.getUser()); Cookie cookie = new Cookie("user", URLEncoder.encode(userJSON, "UTF-8")); @@ -77,9 +76,9 @@ public class SecurityApi { user.setAgreeNDA(agreeNDA); userRepository.save(user); } - - UserActionLogging.LogAction("user agreeNDA",""); - + + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "user agreeNDA", ""); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @@ -106,16 +105,15 @@ public class SecurityApi { */ @RequestMapping(path = "/galaxy", method = RequestMethod.GET, produces = "application/json") - @PreAuthorize("hasRole('Data Manager')") + @PreAuthorize("hasRole('Data Manager')") @ResponseStatus(value = HttpStatus.OK) - public ResponseEntity getGalaxyConfiguration(){ + public ResponseEntity getGalaxyConfiguration() { String stringEncoded = Base64.getEncoder().encodeToString((galaxyUsername + ":" + galaxyPassword).getBytes()); JsonObject object = new JsonObject(); object.addProperty("authorization", stringEncoded); object.addProperty("context", galaxyContext); - UserActionLogging.LogAction("get galaxy information",""); - + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "get galaxy information", ""); + return ResponseEntity.ok(gson.toJson(object)); } - } diff --git a/src/main/java/eu/hbp/mip/controllers/StatsApi.java b/src/main/java/eu/hbp/mip/controllers/StatsApi.java index a92adecde01cb383ce4c677b2476b27afa862437..56d25679321043aec77613e0d028c0332ef49604 100644 --- a/src/main/java/eu/hbp/mip/controllers/StatsApi.java +++ b/src/main/java/eu/hbp/mip/controllers/StatsApi.java @@ -3,16 +3,15 @@ */ package eu.hbp.mip.controllers; -import eu.hbp.mip.utils.UserActionLogging; + 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.UserActionLogging; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; -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.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -24,19 +23,20 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @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() { - UserActionLogging.LogAction("Get statistics (count on users, articles and variables)",""); + public ResponseEntity<GeneralStats> getGeneralStatistics() { + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Get statistics (count on users, articles and variables)", ""); GeneralStats stats = new GeneralStats(); diff --git a/src/main/java/eu/hbp/mip/controllers/UsersApi.java b/src/main/java/eu/hbp/mip/controllers/UsersApi.java index ed3104eb807ab844c25f20df69fac2c200ed0bf2..0620c34e444315dc6c472c304539f9c6b5d8d28d 100644 --- a/src/main/java/eu/hbp/mip/controllers/UsersApi.java +++ b/src/main/java/eu/hbp/mip/controllers/UsersApi.java @@ -4,12 +4,13 @@ package eu.hbp.mip.controllers; -import eu.hbp.mip.utils.UserActionLogging; -import io.swagger.annotations.*; import eu.hbp.mip.model.User; +import eu.hbp.mip.model.UserInfo; import eu.hbp.mip.repositories.UserRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import eu.hbp.mip.utils.UserActionLogging; +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; @@ -27,12 +28,15 @@ public class UsersApi { @Autowired private UserRepository userRepository; + @Autowired + private UserInfo userInfo; + @ApiOperation(value = "Get a user", response = User.class) @RequestMapping(value = "/{username}", method = RequestMethod.GET) public ResponseEntity<User> getAUser( @ApiParam(value = "username", required = true) @PathVariable("username") String username - ) { - UserActionLogging.LogAction("Get a user",""); + ) { + UserActionLogging.LogUserAction(userInfo.getUser().getUsername(), "Get a user", ""); return ResponseEntity.ok(userRepository.findOne(username)); } diff --git a/src/main/java/eu/hbp/mip/model/PathologyDTO.java b/src/main/java/eu/hbp/mip/model/PathologyDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..e447ba6053a6126c66ce20910cc78af07b6988e1 --- /dev/null +++ b/src/main/java/eu/hbp/mip/model/PathologyDTO.java @@ -0,0 +1,77 @@ +package eu.hbp.mip.model; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; + +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; + + @SerializedName("label") + private String label; + + @SerializedName("metadataHierarchy") + private Object metadataHierarchy; + + @SerializedName("datasets") + private List<PathologyDatasetDTO> datasets; + + public static class PathologyDatasetDTO { + @SerializedName("code") + private String code; + + @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; + } + } + +} diff --git a/src/main/java/eu/hbp/mip/utils/ClaimUtils.java b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..f9bd98b5886df396f233e95655f3e6240ac63b47 --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/ClaimUtils.java @@ -0,0 +1,11 @@ +package eu.hbp.mip.utils; + +public class ClaimUtils { + public static String allDatasetsAllowedClaim(){ + return "dataset_all"; + } + + public static String getDatasetClaim(String datasetCode){ + return "dataset_" + datasetCode; + } +} diff --git a/src/main/java/eu/hbp/mip/utils/CustomAccessDeniedHandler.java b/src/main/java/eu/hbp/mip/utils/CustomAccessDeniedHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..783e4a3ff68ab3384a798d9fc70f68f29c114bd6 --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/CustomAccessDeniedHandler.java @@ -0,0 +1,33 @@ +package eu.hbp.mip.utils; + +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; +import java.sql.Timestamp; + +public class CustomAccessDeniedHandler implements AccessDeniedHandler { + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, + AccessDeniedException accessDeniedException) throws IOException, ServletException { + 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/utils/JSONUtil.java b/src/main/java/eu/hbp/mip/utils/JSONUtil.java deleted file mode 100644 index 5822a44053790772138f53a6e388bbfd4e6d888a..0000000000000000000000000000000000000000 --- a/src/main/java/eu/hbp/mip/utils/JSONUtil.java +++ /dev/null @@ -1,31 +0,0 @@ -package eu.hbp.mip.utils; - -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Created by mirco on 01.07.16. - */ -public class JSONUtil { - - private static final Logger LOGGER = LoggerFactory.getLogger(JSONUtil.class); - - private JSONUtil() { - /* Hide implicit public constructor */ - throw new IllegalAccessError("JSONUtil class"); - } - - public static boolean isJSONValid(String test) { - try { - new JsonParser().parse(test); - } catch (JsonParseException jpe) - { - LOGGER.trace("Cannot parse to json", jpe); // This is the normal behavior when the input string is not JSON-ified - return false; - } - return true; - } - -} diff --git a/src/main/java/eu/hbp/mip/utils/UserActionLogging.java b/src/main/java/eu/hbp/mip/utils/UserActionLogging.java index b4a96fc864a16063c39dede8ea0233fbfd41b981..a0ec02277d0249c92577a3fad044de506ab7b039 100644 --- a/src/main/java/eu/hbp/mip/utils/UserActionLogging.java +++ b/src/main/java/eu/hbp/mip/utils/UserActionLogging.java @@ -2,24 +2,21 @@ package eu.hbp.mip.utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.core.context.SecurityContextHolder; public class UserActionLogging { - private static final Logger LOGGER = LoggerFactory.getLogger(UserActionLogging.class); - public static void LogAction(String actionName, String actionIdInfo) - { - LOGGER.info( " User : " - + SecurityContextHolder.getContext().getAuthentication().getName() + public static void LogUserAction(String userName, String actionName, String actionInfo) { + LOGGER.info(" User : " + + userName + " called endpoint: " + actionName - + " info: " + actionIdInfo); + + " info: " + actionInfo); } - // Used from Threads because LogAction won't work. - public static void LogThreadAction(String actionName, String actionIdInfo) - { - LOGGER.info( "Thread -->" + actionName + " info: " + actionIdInfo); + // Usually, used from Threads because threads can't get userName. + // Also used when a user is not authorised yet + public static void LogAction(String actionName, String actionIdInfo) { + LOGGER.info("Action -->" + actionName + " info: " + actionIdInfo); } } diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index efd7a874adcd194ee0145465dca9c08c1f3f5c5d..d4a1e4765b7d25b98ccaf68141e28784a6ff09cb 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -1,6 +1,6 @@ <configuration> <appender name="FILE1" class="ch.qos.logback.core.FileAppender"> - <file>logs/log1.txt</file> + <file>logs/portal-backend.txt</file> <append>true</append> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %msg%n</pattern>