diff --git a/build.sh b/build.sh index 5df0e1ce8b819d6e60cc939b9bf32b1ff95bad9f..84efdf7b37f26e7a7083bbb402aeced4609a152e 100755 --- a/build.sh +++ b/build.sh @@ -37,6 +37,7 @@ docker build --build-arg BUILD_DATE=$(date -Iseconds) \ --tag "$IMAGE:$VERSION" \ . + BUGSNAG_KEY="" eval $(grep -e "^\\s*BUGSNAG_KEY" Dockerfile | tr '\\' ' ') diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index e3a624a13a6c1588210c0546923888956fe45a9f..e34a00c0c2d4b693fc1a4ff26d3f709b43223299 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -30,7 +30,8 @@ hbp: clientSecret: {{ .Env.CLIENT_SECRET }} accessTokenUri: {{ default .Env.TOKEN_URI "https://services.humanbrainproject.eu/oidc/token" }} userAuthorizationUri: {{ default .Env.AUTH_URI "https://services.humanbrainproject.eu/oidc/authorize" }} - tokenName: oauth_token + logoutUri: {{ .Env.LOGOUT_URI }} + tokenName: access_token authenticationScheme: query clientAuthenticationScheme: form useCurrentUri: false @@ -46,12 +47,13 @@ frontend: loginUrl: {{ default .Env.FRONTEND_LOGIN_URL "http://frontend/services/login/hbp" }} redirectAfterLogoutUrl: {{ default .Env.FRONTEND_AFTER_LOGOUT_URL (default .Env.LOGIN_URI "http://frontend/services/login/hbp") }} redirectAfterLoginUrl: {{ default .Env.FRONTEND_AFTER_LOGIN_URL "http://frontend/home" }} - + logging: level: root: {{ default .Env.LOG_LEVEL "INFO" }} org: springframework: + security: DEBUG web: {{ default .Env.LOGGING_LEVEL_WEB "WARN" }} web.servlet.handler.BeanNameUrlHandlerMapping: WARN hibernate: {{ default .Env.LOGGING_LEVEL_HIBERNATE "WARN" }} @@ -86,4 +88,5 @@ services: galaxyUsername: {{ default .Env.GALAXY_USERNAME "admin" }} galaxyPassword: {{ default .Env.GALAXY_PASSWORD "password" }} galaxyContext: {{ default .Env.GALAXY_CONTEXT "nativeGalaxy" }} - + keycloak: + keycloakUrl: {{ .Env.KEYCLOAK_URL }} diff --git a/pom.xml b/pom.xml index 33287a67edb208121a0935c13ae7ab66d8690f7b..674ec19543bfb73623cdf88324f209ae513aa0c3 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,7 @@ <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.8.RELEASE</version> <relativePath/> + </parent> <properties> @@ -65,30 +66,15 @@ </repositories> <dependencies> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-log4j2</artifactId> - </dependency> + <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> - <exclusions> - <exclusion> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-logging</artifactId> - </exclusion> - </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>${spring-boot-starter-actuator.version}</version> - <exclusions> - <exclusion> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-logging</artifactId> - </exclusion> - </exclusions> </dependency> <dependency> <groupId>org.springframework.data</groupId> @@ -98,22 +84,11 @@ <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> - <exclusions> - <exclusion> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-logging</artifactId> - </exclusion> - </exclusions> + </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> - <exclusions> - <exclusion> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-logging</artifactId> - </exclusion> - </exclusions> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> @@ -145,12 +120,6 @@ <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> - <exclusions> - <exclusion> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-logging</artifactId> - </exclusion> - </exclusions> <scope>test</scope> </dependency> <dependency> @@ -249,6 +218,11 @@ <artifactId>java-jwt</artifactId> <version>3.8.3</version> </dependency> + <dependency> + <groupId>com.auth0</groupId> + <artifactId>java-jwt</artifactId> + <version>3.8.3</version> + </dependency> </dependencies> <build> diff --git a/src/main/java/eu/hbp/mip/MIPApplication.java b/src/main/java/eu/hbp/mip/MIPApplication.java index df70ba78a5ba397676f1ad1533895e86a67c4f89..befb16ec91ee35a0bbd2c9a7631f30be9cde4b21 100644 --- a/src/main/java/eu/hbp/mip/MIPApplication.java +++ b/src/main/java/eu/hbp/mip/MIPApplication.java @@ -4,14 +4,23 @@ package eu.hbp.mip; + import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; @SpringBootApplication public class MIPApplication { + private static final Logger LOGGER = LoggerFactory.getLogger(MIPApplication.class); + public static void main(String[] args) { SpringApplication.run(MIPApplication.class, args); } + + + } diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index d8ec1acfbc932f8cb5faa1a42199904ffc88a092..a92879577926739b703e4d015a1a64caa1ed5b87 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -4,10 +4,12 @@ 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.security.oauth2.resource.AuthoritiesExtractor; import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices; import org.springframework.boot.context.embedded.FilterRegistrationBean; @@ -34,6 +36,10 @@ import org.springframework.security.web.csrf.CsrfTokenRepository; import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; 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.servlet.Filter; import javax.servlet.FilterChain; @@ -42,179 +48,324 @@ import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +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 -/** - * Configuration for security. - */ @Configuration @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 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 { - // @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() - .anyRequest().authenticated() - .and().exceptionHandling().authenticationEntryPoint(new CustomLoginUrlAuthenticationEntryPoint(loginUrl)) - .and().logout().addLogoutHandler(new CustomLogoutHandler()).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; + + + +// @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 { 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; - } + 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 + public FilterRegistrationBean oauth2ClientFilterRegistration( + OAuth2ClientContextFilter filter) { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(filter); + registration.setOrder(-100); + return registration; + } - @Bean(name="hbp") - @ConfigurationProperties("hbp.client") - public OAuth2ProtectedResourceDetails hbp() { - return new AuthorizationCodeResourceDetails(); - } + @Bean(name="hbp") + @ConfigurationProperties("hbp.client") + public BaseOAuth2ProtectedResourceDetails hbp() { + return new AuthorizationCodeResourceDetails(); + } - @Bean(name="hbpResource") - @ConfigurationProperties("hbp.resource") - public ResourceServerProperties hbpResource() { - return new ResourceServerProperties(); - } + @Bean(name="hbpResource") + @ConfigurationProperties("hbp.resource") + public ResourceServerProperties hbpResource() { + return new ResourceServerProperties(); + } - public boolean isAuthentication() { - return authentication; - } + public boolean isAuthentication() { + return authentication; + } - public String getFrontendRedirectAfterLogin() { - return frontendRedirectAfterLogin; - } + 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 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); + } - private CsrfTokenRepository csrfTokenRepository() { - HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository(); - repository.setHeaderName("X-XSRF-TOKEN"); - return repository; + 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(); } - private class CustomLogoutHandler implements LogoutHandler { + + public class KeycloakAuthoritiesExtractor + implements AuthoritiesExtractor { + @Override - public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) { + public List<GrantedAuthority> extractAuthorities + (Map<String, Object> map) { + return AuthorityUtils + .commaSeparatedStringToAuthorityList(asAuthorities(map)); + } - // Hackish way of accessing to this information... - final UserInfo userInfo = (UserInfo) httpServletRequest.getSession().getAttribute("userInfo"); - if (userInfo != null) { - userInfo.setFakeAuth(false); + private String asAuthorities(Map<String, Object> map) { + List<String> authorities = new ArrayList<>(); +// authorities.add("BAELDUNG_USER"); + List<LinkedHashMap<String, String>> authz; + authz = (List<LinkedHashMap<String, String>>) map.get("authorities"); + for (LinkedHashMap<String, String> entry : authz) { + authorities.add(entry.get("authority")); } + return String.join(",", authorities); + } + } + + + private LogoutHandler authLogoutHandler() { + return (request, response, authentication) -> { + logout(); + }; + } + + + public void logout() { + // POSTã™ã‚‹ãƒªã‚¯ã‚¨ã‚¹ãƒˆãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã‚’ä½œæˆ + 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リクエストé€ä¿¡ï¼ˆãƒã‚°ã‚¢ã‚¦ãƒˆå®Ÿè¡Œï¼‰ - if (oauth2ClientContext == null || oauth2ClientContext.getAccessToken() == null) - { - return; - } + ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity, String.class); + } + + @Value("#{'${services.keycloak.keycloakUrl}'}") + private String keycloakUrl; + + // static { + // disableCertificateValidation(); + // } - String idToken = oauth2ClientContext.getAccessToken().getAdditionalInformation().get("id_token").toString(); + public void disableCertificateValidation() { + LOGGER.info("disabling certificate validation host : " + keycloakUrl); + // Create a trust manager that does not validate certificate chains + TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + public void checkClientTrusted(X509Certificate[] certs, String authType) {} + public void checkServerTrusted(X509Certificate[] certs, String authType) {} + } }; - 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) + // 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)) { - LOGGER.warn("Cannot send request to OIDC server for revocation ! "); + return true; } - else{ - LOGGER.info("Should be logged out"); + else + { + return false; } - } catch (IOException e) { - LOGGER.warn("Cannot notify logout to OIDC server !"); - LOGGER.trace("Cannot notify logout", e); } + }; + + // Install the all-trusting trust manager + try { + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, trustAllCerts, new SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + HttpsURLConnection.setDefaultHostnameVerifier(hv); + } catch (Exception e) {} + + } - } - } } diff --git a/src/main/java/eu/hbp/mip/controllers/ArticlesApi.java b/src/main/java/eu/hbp/mip/controllers/ArticlesApi.java index 3b567b102c182f529006f055a11112efd5c749a7..963b181576f0626620d76b3a0370dcb840767778 100644 --- a/src/main/java/eu/hbp/mip/controllers/ArticlesApi.java +++ b/src/main/java/eu/hbp/mip/controllers/ArticlesApi.java @@ -10,6 +10,7 @@ import eu.hbp.mip.model.Article; import eu.hbp.mip.model.User; import eu.hbp.mip.model.UserInfo; import eu.hbp.mip.repositories.ArticleRepository; +import eu.hbp.mip.utils.UserActionLogging; import io.swagger.annotations.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,8 +31,6 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @Api(value = "/articles", description = "the articles API") public class ArticlesApi { - private static final Logger LOGGER = LoggerFactory.getLogger(ArticlesApi.class); - @Autowired private UserInfo userInfo; @@ -44,8 +43,6 @@ public class ArticlesApi { @ApiParam(value = "Only ask own articles") @RequestParam(value = "own", required = false) Boolean own, @ApiParam(value = "Only ask results matching status", allowableValues = "draft, published") @RequestParam(value = "status", required = false) String status ) { - LOGGER.info("Get articles"); - User user = userInfo.getUser(); Iterable<Article> articles; @@ -69,7 +66,8 @@ public class ArticlesApi { } } } - + UserActionLogging.LogAction("Get articles", "id : Get All articles"); + return ResponseEntity.ok(articles); } @@ -80,8 +78,7 @@ public class ArticlesApi { public ResponseEntity<Void> addAnArticle( @RequestBody @ApiParam(value = "Article to create", required = true) @Valid Article article ) { - LOGGER.info("Create an article"); - + User user = userInfo.getUser(); article.setCreatedAt(new Date()); @@ -111,7 +108,7 @@ public class ArticlesApi { slug = new Slugify().slugify(article.getTitle()); } catch (IOException e) { slug = ""; - LOGGER.trace("Cannot slugify title", e); + //LOGGER.trace("Cannot slugify title", e); } boolean alreadyExists = true; @@ -130,8 +127,7 @@ public class ArticlesApi { } articleRepository.save(article); - LOGGER.info("Article saved"); - + UserActionLogging.LogAction("Created article", "id : " + article.getSlug()); return new ResponseEntity<>(HttpStatus.CREATED); } @@ -141,7 +137,7 @@ public class ArticlesApi { public ResponseEntity<Article> getAnArticle( @ApiParam(value = "slug", required = true) @PathVariable("slug") String slug ) { - LOGGER.info("Get an article"); + UserActionLogging.LogAction("Getting an article", "id : " + slug); User user = userInfo.getUser(); Article article; @@ -149,7 +145,7 @@ public class ArticlesApi { if(article == null) { - LOGGER.warn("Cannot find article : " + slug); + //LOGGER.warn("Cannot find article : " + slug); return ResponseEntity.badRequest().body(null); } @@ -169,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 ) { - LOGGER.info("Update an article"); + UserActionLogging.LogAction("Update an article", "id : " + slug); User user = userInfo.getUser(); @@ -201,8 +197,7 @@ public class ArticlesApi { articleRepository.save(article); - LOGGER.info("Article updated"); - + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } diff --git a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java index 9f4fee480c37b7b0b0ac8d3fad50ed8e7bf79e59..deef20f755c0fd961e09a238eb662a671a9532e2 100644 --- a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java +++ b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java @@ -20,6 +20,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import eu.hbp.mip.utils.JWTUtil; +import eu.hbp.mip.utils.UserActionLogging; import java.io.IOException; import java.util.*; @@ -33,7 +34,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @Api(value = "/experiments", description = "the experiments API") public class ExperimentApi { - private static final Logger LOGGER = LoggerFactory.getLogger(ExperimentApi.class); + //private static final Logger LOGGER = LoggerFactory.getLogger(ExperimentApi.class); private static final Gson gson = new Gson(); @@ -61,7 +62,7 @@ public class ExperimentApi { @ApiOperation(value = "Create an experiment on Exareme", response = Experiment.class) @RequestMapping(value = "/exareme", method = RequestMethod.POST) public ResponseEntity<String> runExaremeExperiment(@RequestBody ExperimentQuery expQuery) { - LOGGER.info("send ExaremeExperiment"); + //LOGGER.info("send ExaremeExperiment"); Experiment experiment = saveExperiment(expQuery); @@ -87,23 +88,24 @@ public class ExperimentApi { experiment.setHasError(code >= 400); experiment.setHasServerError(code >= 500); } catch (IOException e) { - LOGGER.trace("Invalid UUID", e); - LOGGER.warn("Exareme experiment failed to run properly !"); + //LOGGER.trace("Invalid UUID", e); + //LOGGER.warn("Exareme experiment failed to run properly !"); experiment.setHasError(true); experiment.setHasServerError(true); experiment.setResult(e.getMessage()); } finishExperiment(experiment); }).start(); - + + UserActionLogging.LogAction("create ExaremeExperiment", "no info"); + return new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); } @ApiOperation(value = "Create a workflow", response = Experiment.class) @RequestMapping(value = "/workflow", method = RequestMethod.POST) public ResponseEntity<String> runWorkflow(@RequestBody ExperimentQuery expQuery) { - LOGGER.info("send Workflow"); - + Experiment experiment = saveExperiment(expQuery); String algoCode = expQuery.getAlgorithms().get(0).getCode(); @@ -132,7 +134,7 @@ public class ExperimentApi { experiment.setHasError(code >= 400); experiment.setHasServerError(code >= 500); } catch (IOException e) { - LOGGER.trace("Invalid UUID", e); + //LOGGER.trace("Invalid UUID", e); experiment.setHasError(true); experiment.setHasServerError(true); experiment.setResult(e.getMessage()); @@ -140,6 +142,8 @@ public class ExperimentApi { finishExperiment(experiment); }).start(); + UserActionLogging.LogAction("create workflow", "no info"); + return new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); } @@ -147,15 +151,14 @@ public class ExperimentApi { @RequestMapping(value = "/{uuid}", method = RequestMethod.GET) public ResponseEntity<String> getExperiment( @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - LOGGER.info("Get an experiment"); Experiment experiment; UUID experimentUuid; try { experimentUuid = UUID.fromString(uuid); } catch (IllegalArgumentException iae) { - LOGGER.trace("Invalid UUID", iae); - LOGGER.warn("An invalid Experiment UUID was received ! " + uuid); + //LOGGER.trace("Invalid UUID", iae); + //LOGGER.warn("An invalid Experiment UUID was received ! " + uuid); return ResponseEntity.badRequest().body("Invalid Experiment UUID"); } @@ -164,7 +167,9 @@ public class ExperimentApi { if (experiment == null) { return new ResponseEntity<>("Not found", HttpStatus.NOT_FOUND); } - + + UserActionLogging.LogAction("Get an experiment ", " uuid : "+ uuid); + return new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); } @@ -172,8 +177,9 @@ public class ExperimentApi { @RequestMapping(value = "/workflow/status/{historyId}", method = RequestMethod.GET) public ResponseEntity<String> getWorkflowStatus( @ApiParam(value = "historyId", required = true) @PathVariable("historyId") String historyId) { - LOGGER.info("Get a workflow status"); - + + UserActionLogging.LogAction("Get a workflow status", " historyId : "+ historyId); + String url = workflowUrl + "/getWorkflowStatus/" + historyId; try { User user = userInfo.getUser(); @@ -186,6 +192,7 @@ public class ExperimentApi { } catch (IOException e) { return ResponseEntity.status(500).body(e.getMessage()); } + } // TODO: factorize workflow results @@ -193,8 +200,8 @@ public class ExperimentApi { @RequestMapping(value = "/workflow/results/{historyId}", method = RequestMethod.GET) public ResponseEntity<String> getWorkflowResults( @ApiParam(value = "historyId", required = true) @PathVariable("historyId") String historyId) { - LOGGER.info("Get a workflow results"); - + UserActionLogging.LogAction("Get workflow results", " historyId : "+ historyId); + String url = workflowUrl + "/getWorkflowResults/" + historyId; try { StringBuilder response = new StringBuilder(); @@ -214,8 +221,9 @@ public class ExperimentApi { public ResponseEntity<String> getWorkflowResultBody( @ApiParam(value = "historyId", required = true) @PathVariable("historyId") String historyId, @ApiParam(value = "resultId", required = true) @PathVariable("resultId") String resultId) { - LOGGER.info("Get a workflow result content"); + UserActionLogging.LogAction("Get workflow result content", " historyId : "+ historyId + " resultId : "+ resultId); + String url = workflowUrl + "/getWorkflowResultsBody/" + historyId + "/contents/" + resultId; try { StringBuilder response = new StringBuilder(); @@ -235,7 +243,7 @@ public class ExperimentApi { public ResponseEntity<String> getWorkflowResultsDetails( @ApiParam(value = "historyId", required = true) @PathVariable("historyId") String historyId, @ApiParam(value = "resultId", required = true) @PathVariable("resultId") String resultId) { - LOGGER.info("Get a workflow result content"); + UserActionLogging.LogAction("Get workflow result details", " historyId : "+ historyId + " resultId : "+ resultId); String url = workflowUrl + "/getWorkflowResultsDetails/" + historyId + "/contents/" + resultId; try { @@ -255,7 +263,8 @@ public class ExperimentApi { @RequestMapping(value = "/{uuid}/markAsViewed", method = RequestMethod.GET) public ResponseEntity<String> markExperimentAsViewed( @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - LOGGER.info("Mark an experiment as viewed"); + + UserActionLogging.LogAction("Mark an experiment as viewed", " uuid : "+ uuid); Experiment experiment; UUID experimentUuid; @@ -263,8 +272,8 @@ public class ExperimentApi { try { experimentUuid = UUID.fromString(uuid); } catch (IllegalArgumentException iae) { - LOGGER.trace("Invalid UUID", iae); - LOGGER.warn("An invalid Experiment UUID was received !"); + //LOGGER.trace("Invalid UUID", iae); + //LOGGER.warn("An invalid Experiment UUID was received !"); return ResponseEntity.badRequest().body("Invalid Experiment UUID"); } @@ -274,7 +283,7 @@ public class ExperimentApi { experiment.setResultsViewed(true); experimentRepository.save(experiment); - LOGGER.info("Experiment updated (marked as viewed)"); + UserActionLogging.LogAction("Experiment updated (marked as viewed)", " "); return new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); } @@ -283,8 +292,9 @@ public class ExperimentApi { @RequestMapping(value = "/{uuid}/markAsShared", method = RequestMethod.GET) public ResponseEntity<String> markExperimentAsShared( @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - LOGGER.info("Mark an experiment as shared"); + UserActionLogging.LogAction("Mark an experiment as shared", " uuid : "+ uuid); + return doMarkExperimentAsShared(uuid, true); } @@ -292,8 +302,8 @@ public class ExperimentApi { @RequestMapping(value = "/{uuid}/markAsUnshared", method = RequestMethod.GET) public ResponseEntity<String> markExperimentAsUnshared( @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - LOGGER.info("Mark an experiment as unshared"); - + UserActionLogging.LogAction("Mark an experiment as unshared", " uuid : "+ uuid); + return doMarkExperimentAsShared(uuid, false); } @@ -301,8 +311,9 @@ public class ExperimentApi { @RequestMapping(method = RequestMethod.GET, params = { "maxResultCount" }) public ResponseEntity<String> listExperiments( @ApiParam(value = "maxResultCount") @RequestParam int maxResultCount) { - LOGGER.info("List experiments"); + UserActionLogging.LogAction("List experiments", " maxResultCount : "+ maxResultCount); + return doListExperiments(false, null); } @@ -310,7 +321,8 @@ public class ExperimentApi { @RequestMapping(method = RequestMethod.GET, params = { "slug", "maxResultCount" }) public ResponseEntity<String> listExperiments(@ApiParam(value = "slug") @RequestParam("slug") String modelSlug, @ApiParam(value = "maxResultCount") @RequestParam("maxResultCount") int maxResultCount) { - LOGGER.info("List experiments"); + + UserActionLogging.LogAction("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", @@ -323,7 +335,7 @@ 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) { - LOGGER.info("List my experiments"); + UserActionLogging.LogAction("List my experiments", " mine : "+ mine); return doListExperiments(true, null); } @@ -361,8 +373,8 @@ public class ExperimentApi { try { experimentUuid = UUID.fromString(uuid); } catch (IllegalArgumentException iae) { - LOGGER.trace("Invalid UUID", iae); - LOGGER.warn("An invalid Experiment UUID was received !"); + //LOGGER.trace("Invalid UUID", iae); + //LOGGER.warn("An invalid Experiment UUID was received !"); return ResponseEntity.badRequest().body("Invalid Experiment UUID"); } @@ -373,8 +385,8 @@ public class ExperimentApi { experiment.setShared(shared); experimentRepository.save(experiment); - - LOGGER.info("Experiment updated (marked as shared)"); + + UserActionLogging.LogAction("Experiment updated (marked as shared)", ""); return new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); } @@ -383,7 +395,7 @@ public class ExperimentApi { experiment.setFinished(new Date()); experimentRepository.save(experiment); - LOGGER.info("Experiment updated (finished)"); + UserActionLogging.LogAction("Experiment updated (finished)",""); } private HashMap<String, String> makeObject(String name, String value) { @@ -395,7 +407,6 @@ public class ExperimentApi { } private Experiment saveExperiment(ExperimentQuery expQuery) { - LOGGER.info("saveExperiment"); Experiment experiment = new Experiment(); experiment.setUuid(UUID.randomUUID()); @@ -408,7 +419,7 @@ public class ExperimentApi { experiment.setModel(modelRepository.findOne(expQuery.getModel())); experimentRepository.save(experiment); - LOGGER.info("Experiment saved"); + UserActionLogging.LogAction("Saved an experiment", " id : "+experiment.getUuid()); return experiment; } diff --git a/src/main/java/eu/hbp/mip/controllers/FilesAPI.java b/src/main/java/eu/hbp/mip/controllers/FilesAPI.java index d79717062874418824d305a908926f1c61075255..5acdb38ca88a851cb96a3c375c51165b1e157531 100644 --- a/src/main/java/eu/hbp/mip/controllers/FilesAPI.java +++ b/src/main/java/eu/hbp/mip/controllers/FilesAPI.java @@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; +import eu.hbp.mip.utils.UserActionLogging; import java.time.LocalDateTime; @@ -26,8 +27,7 @@ import java.time.LocalDateTime; @Api(value = "/protected", description = "the protected files API") public class FilesAPI { - private static final Logger LOGGER = LoggerFactory.getLogger(FilesAPI.class); - + @Autowired private UserInfo userInfo; @@ -36,12 +36,12 @@ public class FilesAPI { public ResponseEntity<Void> getProtectedFile( @ApiParam(value = "filename", required = true) @PathVariable("filename") String filename ) { - LOGGER.info("Get protected file"); + UserActionLogging.LogAction("Get protected file", " filename : " + filename); String filepath = "/protected/" + filename; String user = userInfo.getUser().getUsername(); String time = LocalDateTime.now().toString(); - LOGGER.info("User " + user + " downloaded " + filepath + " at "+ time); + UserActionLogging.LogAction("User " + user + " downloaded " + filepath, ""); HttpHeaders headers = new HttpHeaders(); headers.add("X-Accel-Redirect", filepath); diff --git a/src/main/java/eu/hbp/mip/controllers/JWTApi.java b/src/main/java/eu/hbp/mip/controllers/JWTApi.java index a3d939bbba3eb5c5a8666497be9f5c6d6414e446..a1cb6afd55431bb859170b52c9daa7007ca69c42 100644 --- a/src/main/java/eu/hbp/mip/controllers/JWTApi.java +++ b/src/main/java/eu/hbp/mip/controllers/JWTApi.java @@ -13,14 +13,13 @@ import org.springframework.web.bind.annotation.*; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import eu.hbp.mip.utils.JWTUtil; +import eu.hbp.mip.utils.UserActionLogging; @RestController @RequestMapping(value = "/jwt", produces = { TEXT_PLAIN_VALUE }) @Api(value = "/jwt", description = "the jwt API") public class JWTApi { - private static final Logger LOGGER = LoggerFactory.getLogger(JWTApi.class); - @Autowired private UserInfo userInfo; @@ -31,7 +30,7 @@ public class JWTApi { @RequestMapping(method = RequestMethod.POST) public ResponseEntity<String> createJWT() { - LOGGER.info("Create a JSON Web Token"); + UserActionLogging.LogAction("Create a JSON Web Token", ""); User user = userInfo.getUser(); String token = JWTUtil.getJWT(jwtSecret, user.getEmail()); diff --git a/src/main/java/eu/hbp/mip/controllers/MethodsApi.java b/src/main/java/eu/hbp/mip/controllers/MethodsApi.java index fe7e8cd4535ac8af92603be12d40fad2bd264a74..f88f6795156f566b4b4e6e70288a5e4d535a67da 100644 --- a/src/main/java/eu/hbp/mip/controllers/MethodsApi.java +++ b/src/main/java/eu/hbp/mip/controllers/MethodsApi.java @@ -15,14 +15,14 @@ import java.io.IOException; import eu.hbp.mip.utils.JWTUtil; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import org.springframework.beans.factory.annotation.Autowired; +import eu.hbp.mip.utils.UserActionLogging; @RestController @RequestMapping(value = "/methods", produces = { APPLICATION_JSON_VALUE }) @Api(value = "/methods", description = "the methods API") public class MethodsApi { - private static final Logger LOGGER = LoggerFactory.getLogger(MethodsApi.class); - + private static final Gson gson = new Gson(); @Value("#{'${services.exareme.algorithmsUrl:http://localhost:9090/mining/algorithms.json}'}") @@ -40,7 +40,7 @@ public class MethodsApi { @ApiOperation(value = "List Exareme algorithms and validations", response = String.class) @RequestMapping(value = "/exareme", method = RequestMethod.GET) public ResponseEntity<Object> getExaremeAlgorithms() { - LOGGER.info("List Exareme algorithms and validations"); + UserActionLogging.LogAction("List Exareme algorithms and validations", ""); try { StringBuilder response = new StringBuilder(); @@ -57,7 +57,7 @@ public class MethodsApi { @ApiOperation(value = "List Galaxy workflows", response = String.class) @RequestMapping(value = "/workflows", method = RequestMethod.GET) public ResponseEntity<Object> getWorkflows() { - LOGGER.info("List Galaxy workflows"); + UserActionLogging.LogAction("List Galaxy workflows", ""); try { User user = userInfo.getUser(); diff --git a/src/main/java/eu/hbp/mip/controllers/MiningApi.java b/src/main/java/eu/hbp/mip/controllers/MiningApi.java index e34b317d48e37cdea220bc447c1b1a853b2ee8ed..13272fc7355ed509e5b030429409a98b83ac55e1 100644 --- a/src/main/java/eu/hbp/mip/controllers/MiningApi.java +++ b/src/main/java/eu/hbp/mip/controllers/MiningApi.java @@ -21,6 +21,9 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; + +import eu.hbp.mip.utils.UserActionLogging; + import org.springframework.web.bind.annotation.*; @@ -37,7 +40,6 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @Api(value = "/mining", description = "the mining API") public class MiningApi { - private static final Logger LOGGER = LoggerFactory.getLogger(MiningApi.class); private static final Gson gson = new Gson(); @Autowired @@ -49,7 +51,7 @@ public class MiningApi { @ApiOperation(value = "Create an histogram on Exareme", response = String.class) @RequestMapping(value = "/exareme", method = RequestMethod.POST) public ResponseEntity runExaremeMining(@RequestBody List<HashMap<String, String>> queryList) { - LOGGER.info("Run an histogram"); + UserActionLogging.LogAction("Run an histogram", ""); String query = gson.toJson(queryList); String url = miningExaremeQueryUrl + "/" + "HISTOGRAMS"; @@ -67,7 +69,7 @@ public class MiningApi { @ApiOperation(value = "Create an descriptive statistic on Exareme", response = String.class) @RequestMapping(value = "/exareme-stats", method = RequestMethod.POST) public ResponseEntity runExaremeDescriptiveStats(@RequestBody List<HashMap<String, String>> queryList) { - LOGGER.info("Run descriptive stats"); + UserActionLogging.LogAction("Run descriptive stats", ""); String query = gson.toJson(queryList); String url = miningExaremeQueryUrl + "/" + "DESCRIPTIVE_STATS"; @@ -88,7 +90,7 @@ public class MiningApi { @RequestBody List<HashMap<String, String>> queryList, @ApiParam(value = "algorithmName", required = true) @PathVariable("algorithmName") String algorithmName ) { - LOGGER.info("Run algo"); + UserActionLogging.LogAction("Run algo", ""); String query = gson.toJson(queryList); String url = miningExaremeQueryUrl + "/" + algorithmName; diff --git a/src/main/java/eu/hbp/mip/controllers/ModelsApi.java b/src/main/java/eu/hbp/mip/controllers/ModelsApi.java index 56bc1ce10bf5c4f322349720b69d48f6b14f61fe..02ee9ac6b25b353876566a7261a7e6deba962574 100644 --- a/src/main/java/eu/hbp/mip/controllers/ModelsApi.java +++ b/src/main/java/eu/hbp/mip/controllers/ModelsApi.java @@ -17,6 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import eu.hbp.mip.utils.UserActionLogging; import java.io.IOException; import java.util.*; @@ -28,7 +29,6 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @Api(value = "/models", description = "the models API") public class ModelsApi { - private static final Logger LOGGER = LoggerFactory.getLogger(ModelsApi.class); @Autowired private UserInfo userInfo; @@ -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 ) { - LOGGER.info("Get models"); + UserActionLogging.LogAction("Get models",""); User user = userInfo.getUser(); @@ -98,7 +98,7 @@ public class ModelsApi { @RequestBody @ApiParam(value = "Model to create", required = true) Model model ) { - LOGGER.info("Create a model"); + UserActionLogging.LogAction("Create a model",""); User user = userInfo.getUser(); @@ -129,7 +129,7 @@ public class ModelsApi { } modelRepository.save(model); - LOGGER.info("Model saved (also saved model.config and model.query)"); + UserActionLogging.LogAction("Model saved (also saved model.config and model.query)"," id : " + model.getSlug()); return ResponseEntity.status(HttpStatus.CREATED).body(model); } @@ -165,7 +165,7 @@ public class ModelsApi { slug = new Slugify().slugify(title); } catch (IOException e) { slug = ""; // Should never happen - LOGGER.trace("Cannot slugify title", e); + //LOGGER.trace("Cannot slugify title", e); } return slug; } @@ -192,7 +192,7 @@ public class ModelsApi { public ResponseEntity<Model> getAModel( @ApiParam(value = "slug", required = true) @PathVariable("slug") String slug ) { - LOGGER.info("Get a model"); + UserActionLogging.LogAction("Get a model", " id : " + slug); User user = userInfo.getUser(); @@ -200,7 +200,7 @@ public class ModelsApi { if(model == null) { - LOGGER.warn("Cannot find model : " + slug); + //LOGGER.warn("Cannot find model : " + slug); return ResponseEntity.badRequest().body(null); } @@ -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 ) { - LOGGER.info("Update a model"); + UserActionLogging.LogAction("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); - LOGGER.info("Model updated (also saved/updated model.config and model.query)"); + UserActionLogging.LogAction("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 59511de85016213a66eb576e8ea7f8410d5d6b0f..27f635d3278e9a2990489b4a3c01bd0d9851c34a 100644 --- a/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java +++ b/src/main/java/eu/hbp/mip/controllers/PathologiesApi.java @@ -18,6 +18,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import eu.hbp.mip.utils.UserActionLogging; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @@ -28,6 +29,8 @@ public class PathologiesApi { @RequestMapping(name = "/pathologies", method = RequestMethod.GET) public String getPathologies() { + UserActionLogging.LogAction("load the pathologies", ""); + return loadPathologies(); } diff --git a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java index cfddbd334d3070cb433672b93b3bc0d205339e10..0f93c437ea8477000e1881815cce984eca36f3f8 100644 --- a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java +++ b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java @@ -17,6 +17,8 @@ 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.prepost.PreAuthorize; +import eu.hbp.mip.utils.UserActionLogging; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; @@ -29,8 +31,6 @@ import java.util.Base64; @RestController public class SecurityApi { - private static final Logger LOGGER = LoggerFactory.getLogger(SecurityApi.class); - private static final Gson gson = new Gson(); @Autowired @@ -45,7 +45,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",""); try { String userJSON = mapper.writeValueAsString(userInfo.getUser()); Cookie cookie = new Cookie("user", URLEncoder.encode(userJSON, "UTF-8")); @@ -53,7 +54,7 @@ public class SecurityApi { cookie.setPath("/"); response.addCookie(cookie); } catch (JsonProcessingException | UnsupportedEncodingException e) { - LOGGER.trace("Cannot read user json", e); + //LOGGER.trace("Cannot read user json", e); } if (!securityConfiguration.isAuthentication()) { @@ -76,6 +77,9 @@ public class SecurityApi { user.setAgreeNDA(agreeNDA); userRepository.save(user); } + + UserActionLogging.LogAction("user agreeNDA",""); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @@ -102,13 +106,15 @@ public class SecurityApi { */ @RequestMapping(path = "/galaxy", method = RequestMethod.GET, produces = "application/json") + @PreAuthorize("hasRole('Data Manager')") @ResponseStatus(value = HttpStatus.OK) 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",""); + 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 d09c032bc75659c18100a4f58ca3965c2e4b4933..a92adecde01cb383ce4c677b2476b27afa862437 100644 --- a/src/main/java/eu/hbp/mip/controllers/StatsApi.java +++ b/src/main/java/eu/hbp/mip/controllers/StatsApi.java @@ -3,7 +3,7 @@ */ package eu.hbp.mip.controllers; - +import eu.hbp.mip.utils.UserActionLogging; import eu.hbp.mip.model.GeneralStats; import eu.hbp.mip.repositories.ArticleRepository; import eu.hbp.mip.repositories.UserRepository; @@ -25,8 +25,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @Api(value = "/stats", description = "the stats API") public class StatsApi { - private static final Logger LOGGER = LoggerFactory.getLogger(StatsApi.class); - + @Autowired private UserRepository userRepository; @@ -37,7 +36,7 @@ public class StatsApi { @ApiOperation(value = "Get general statistics", response = GeneralStats.class) @RequestMapping(method = RequestMethod.GET) public ResponseEntity<GeneralStats> getGeneralStatistics() { - LOGGER.info("Get statistics (count on users, articles and variables)"); + UserActionLogging.LogAction("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 89767017e0557b231e200006f8e330d9ca44ee9c..ed3104eb807ab844c25f20df69fac2c200ed0bf2 100644 --- a/src/main/java/eu/hbp/mip/controllers/UsersApi.java +++ b/src/main/java/eu/hbp/mip/controllers/UsersApi.java @@ -4,6 +4,7 @@ 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.repositories.UserRepository; @@ -23,8 +24,6 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @Api(value = "/users", description = "the users API") public class UsersApi { - private static final Logger LOGGER = LoggerFactory.getLogger(UsersApi.class); - @Autowired private UserRepository userRepository; @@ -33,7 +32,7 @@ public class UsersApi { public ResponseEntity<User> getAUser( @ApiParam(value = "username", required = true) @PathVariable("username") String username ) { - LOGGER.info("Get a user"); + UserActionLogging.LogAction("Get a user",""); return ResponseEntity.ok(userRepository.findOne(username)); } diff --git a/src/main/java/eu/hbp/mip/utils/UserActionLogging.java b/src/main/java/eu/hbp/mip/utils/UserActionLogging.java new file mode 100644 index 0000000000000000000000000000000000000000..745fc24a6ca53951c1f43cd9db2f6fa5965a73d4 --- /dev/null +++ b/src/main/java/eu/hbp/mip/utils/UserActionLogging.java @@ -0,0 +1,23 @@ +package eu.hbp.mip.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.context.SecurityContextHolder; + +import java.time.LocalTime; + +public class UserActionLogging { + + + private static final Logger LOGGER = LoggerFactory.getLogger(UserActionLogging.class); + + public static void LogAction(String actionName, String actionIdInfo) + { + LOGGER.info( LocalTime.now()+" User : " + + SecurityContextHolder.getContext().getAuthentication().getName() + + " called enpoint " + actionName + + " info " + + actionIdInfo); + } + +} diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000000000000000000000000000000000000..efd7a874adcd194ee0145465dca9c08c1f3f5c5d --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,19 @@ +<configuration> + <appender name="FILE1" class="ch.qos.logback.core.FileAppender"> + <file>logs/log1.txt</file> + <append>true</append> + <encoder> + <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %msg%n</pattern> + </encoder> + </appender> + + <logger name="eu.hbp.mip"> + <appender-ref ref="FILE1" /> + </logger> + <logger name="org.springframework"> + <appender-ref ref="FILE1" /> + </logger> + <logger name="eu.hbp.mip.utils" level="INFO" additivity="false"> + <appender-ref ref="FILE1" /> + </logger> +</configuration> \ No newline at end of file