From c5cb48f99562d22bb83c2bdb3e4b3f6df95c8149 Mon Sep 17 00:00:00 2001 From: unknown <Jerrypan44@gmail.com> Date: Tue, 5 Nov 2019 15:35:59 +0200 Subject: [PATCH 1/9] initial wip --- pom.xml | 12 ++++++++++++ src/main/application.properties | 14 ++++++++++++++ .../mip/configuration/SecurityConfiguration.java | 11 ++++++++--- 3 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 src/main/application.properties diff --git a/pom.xml b/pom.xml index 33287a67e..1d8ac0d6c 100644 --- a/pom.xml +++ b/pom.xml @@ -249,6 +249,18 @@ <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> + <dependency> + <groupId>org.keycloak</groupId> + <artifactId>keycloak-spring-boot-2-starter</artifactId> + <version>4.0.0.Final</version> + </dependency> + + </dependencies> <build> diff --git a/src/main/application.properties b/src/main/application.properties new file mode 100644 index 000000000..a2a68cdc6 --- /dev/null +++ b/src/main/application.properties @@ -0,0 +1,14 @@ +keycloak.realm=Demo +keycloak.resource=vanilla +keycloak.auth-server-url=http://localhost:8080/auth +keycloak.ssl-required=external +keycloak.public-client=true + +#keycloak.securityConstraints[0].authRoles[0]=Member +#keycloak.securityConstraints[0].authRoles[1]=Librarian +#keycloak.securityConstraints[0].securityCollections[0].name=member resource +#keycloak.securityConstraints[0].securityCollections[0].patterns[0]=/books + +#keycloak.securityConstraints[1].authRoles[0]=Librarian +#keycloak.securityConstraints[1].securityCollections[0].name=librarian resource +#keycloak.securityConstraints[1].securityCollections[0].patterns[0]=/manager \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index d8ec1acfb..adacd7a81 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -88,6 +88,9 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @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 { // @formatter:off @@ -108,9 +111,11 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class); } else { - http.antMatcher("/**") - .authorizeRequests() - .antMatchers("/**").permitAll().and().csrf().disable(); + //keycloak + (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName()); +// http.antMatcher("/**") +// .authorizeRequests() +// .antMatchers("/**").permitAll().and().csrf().disable(); } } -- GitLab From eb06a4b0cdae8563c20718a817a28db859347853 Mon Sep 17 00:00:00 2001 From: unknown <Jerrypan44@gmail.com> Date: Tue, 5 Nov 2019 16:24:57 +0200 Subject: [PATCH 2/9] change configuration --- src/main/application.properties | 14 -------------- src/main/resources/application.conf | 8 ++++++++ 2 files changed, 8 insertions(+), 14 deletions(-) delete mode 100644 src/main/application.properties diff --git a/src/main/application.properties b/src/main/application.properties deleted file mode 100644 index a2a68cdc6..000000000 --- a/src/main/application.properties +++ /dev/null @@ -1,14 +0,0 @@ -keycloak.realm=Demo -keycloak.resource=vanilla -keycloak.auth-server-url=http://localhost:8080/auth -keycloak.ssl-required=external -keycloak.public-client=true - -#keycloak.securityConstraints[0].authRoles[0]=Member -#keycloak.securityConstraints[0].authRoles[1]=Librarian -#keycloak.securityConstraints[0].securityCollections[0].name=member resource -#keycloak.securityConstraints[0].securityCollections[0].patterns[0]=/books - -#keycloak.securityConstraints[1].authRoles[0]=Librarian -#keycloak.securityConstraints[1].securityCollections[0].name=librarian resource -#keycloak.securityConstraints[1].securityCollections[0].patterns[0]=/manager \ No newline at end of file diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index e2abfd5ae..61b39c873 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -6,6 +6,14 @@ app { clusterSystemName = ${clustering.cluster.name} } +keycloak { + realm=Demo + resource=vanilla + auth-server-url=http://localhost:8080/auth + ssl-required=external + public-client=true +} + clustering { ip = "127.0.0.1" ip = ${?CLUSTER_IP} -- GitLab From 071465bebde8075ec33c50a6e5e1d9f0ed156339 Mon Sep 17 00:00:00 2001 From: unknown <Jerrypan44@gmail.com> Date: Wed, 6 Nov 2019 14:48:25 +0200 Subject: [PATCH 3/9] keycloak wip --- .../mip/configuration/KeycloakConfiguration.java | 14 ++++++++++++++ .../mip/configuration/SecurityConfiguration.java | 6 +++--- 2 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 src/main/java/eu/hbp/mip/configuration/KeycloakConfiguration.java diff --git a/src/main/java/eu/hbp/mip/configuration/KeycloakConfiguration.java b/src/main/java/eu/hbp/mip/configuration/KeycloakConfiguration.java new file mode 100644 index 000000000..45c598924 --- /dev/null +++ b/src/main/java/eu/hbp/mip/configuration/KeycloakConfiguration.java @@ -0,0 +1,14 @@ +package eu.hbp.mip.configuration; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import org.keycloak.KeycloakSecurityContext; + +public class KeycloakConfiguration { + + @Autowired + private HttpServletRequest request; + public KeycloakSecurityContext getKeycloakSecurityContext() { + return (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName()); + } +} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index adacd7a81..3d0a49357 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -88,8 +88,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Value("#{'${hbp.resource.revokeTokenUri:https://services.humanbrainproject.eu/oidc/revoke}'}") private String revokeTokenURI; - @Autowired - private HttpServletRequest request; +// @Autowired +// private HttpServletRequest request; @Override protected void configure(HttpSecurity http) throws Exception { @@ -112,7 +112,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { } else { //keycloak - (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName()); + KeycloakConfiguration.getKeycloakSecurityContext(); // http.antMatcher("/**") // .authorizeRequests() // .antMatchers("/**").permitAll().and().csrf().disable(); -- GitLab From a8a6d692d56b25057f38fdaef3ebca12728613b7 Mon Sep 17 00:00:00 2001 From: unknown <Jerrypan44@gmail.com> Date: Mon, 11 Nov 2019 18:01:06 +0200 Subject: [PATCH 4/9] keycloak wip --- build.sh | 4 +- pom.xml | 15 +- .../configuration/SecurityConfiguration.java | 367 ++++++++++-------- 3 files changed, 218 insertions(+), 168 deletions(-) diff --git a/build.sh b/build.sh index 5df0e1ce8..752b95924 100755 --- a/build.sh +++ b/build.sh @@ -34,9 +34,9 @@ docker build --build-arg BUILD_DATE=$(date -Iseconds) \ --build-arg VCS_REF=$VCS_REF \ --build-arg VERSION=$VERSION \ --tag "$IMAGE:latest" \ - --tag "$IMAGE:$VERSION" \ + --tag "jerrypan44/portal-backend:latest" \ . - +docker push "jerrypan44/portal-backend:latest" BUGSNAG_KEY="" eval $(grep -e "^\\s*BUGSNAG_KEY" Dockerfile | tr '\\' ' ') diff --git a/pom.xml b/pom.xml index 1d8ac0d6c..d206d2ad2 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> @@ -256,8 +257,7 @@ </dependency> <dependency> <groupId>org.keycloak</groupId> - <artifactId>keycloak-spring-boot-2-starter</artifactId> - <version>4.0.0.Final</version> + <artifactId>keycloak-spring-boot-starter</artifactId> </dependency> @@ -354,4 +354,15 @@ </plugins> </build> + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.keycloak.bom</groupId> + <artifactId>keycloak-adapter-bom</artifactId> + <version>7.0.0</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> </project> diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index 3d0a49357..ba6a380de 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -49,177 +49,216 @@ import java.io.IOException; /** * Configuration for security. */ -@Configuration -@EnableOAuth2Client -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { - - private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfiguration.class); - +@KeycloakConfiguration +public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter { @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; + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth.authenticationProvider(keycloakAuthenticationProvider()); + } /** - * URL to revoke auth token + * Defines the session authentication strategy. */ - @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 { - // @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 { - //keycloak - KeycloakConfiguration.getKeycloakSecurityContext(); -// 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 OAuth2ProtectedResourceDetails 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); - } - }; + @Override + protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { + return new NullAuthenticatedSessionStrategy(); } - private CsrfTokenRepository csrfTokenRepository() { - HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository(); - repository.setHeaderName("X-XSRF-TOKEN"); - return repository; + @Override + protected void configure(HttpSecurity http) throws Exception + { + super.configure(http); + http + .authorizeRequests() + .antMatchers("/user*").authenticated() + .antMatchers("/public1*").hasAuthority("public13") + .antMatchers("/public2*").hasAuthority("public2") + .antMatchers("/public3*").hasAuthority("public3") + .antMatchers("/admin*").authenticated() + .anyRequest().permitAll() + .and() + .logout() + .addLogoutHandler(keycloakLogoutHandler())//.logoutRequestMatcher(new AntPathRequestMatcher("/logout")) + .logoutUrl("/logout").permitAll() + .logoutSuccessUrl("/"); +// .anyRequest().permitAll().and().logout().addLogoutHandler(new KeycloakLogoutHandler(new RestTemplate())).logoutRequestMatcher(new AntPathRequestMatcher("/logout")); + ; } - 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); - } - - } - } } + +//@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; +// +//// @Autowired +//// private HttpServletRequest request; +// +// @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 { +// //keycloak +// //KeycloakConfiguration.getKeycloakSecurityContext(); +//// 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 OAuth2ProtectedResourceDetails 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); +// } +// +// } +// } +//} -- GitLab From 9e28626ec653e845442dd33d505df2a1b442920d Mon Sep 17 00:00:00 2001 From: jerrypan44 <jerrypan44@gmail.com> Date: Tue, 12 Nov 2019 11:29:31 +0000 Subject: [PATCH 5/9] adding build file --- build.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 5df0e1ce8..beeee73c9 100755 --- a/build.sh +++ b/build.sh @@ -34,8 +34,10 @@ docker build --build-arg BUILD_DATE=$(date -Iseconds) \ --build-arg VCS_REF=$VCS_REF \ --build-arg VERSION=$VERSION \ --tag "$IMAGE:latest" \ - --tag "$IMAGE:$VERSION" \ + --tag "jerrypan44/portal-backend:latest" \ . + +docker push "jerrypan44/portal-backend:latest" BUGSNAG_KEY="" eval $(grep -e "^\\s*BUGSNAG_KEY" Dockerfile | tr '\\' ' ') -- GitLab From 2b7084920c5e49cd35b37adf1b3c619e10db30d6 Mon Sep 17 00:00:00 2001 From: jerrypan44 <jerrypan44@gmail.com> Date: Fri, 22 Nov 2019 15:35:02 +0000 Subject: [PATCH 6/9] WIP committed working features roles and keycloak integration --- docker/config/application.tmpl | 5 +- pom.xml | 6 - .../configuration/KeycloakConfiguration.java | 14 - .../configuration/SecurityConfiguration.java | 417 +++++++++--------- .../eu/hbp/mip/controllers/SecurityApi.java | 2 + 5 files changed, 213 insertions(+), 231 deletions(-) delete mode 100644 src/main/java/eu/hbp/mip/configuration/KeycloakConfiguration.java diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index e3a624a13..c05c497c1 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -30,11 +30,11 @@ 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 + tokenName: access_token authenticationScheme: query clientAuthenticationScheme: form useCurrentUri: false - preEstablishedRedirectUri: {{ default .Env.FRONTEND_LOGIN_URL "http://frontend/services/login/hbp" }} + preEstablishedRedirectUri: {{ default .Env.FRONTEND_LOGIN_URL "http://localhost:8080/services/login/hbp" }} resource: userInfoUri: {{ default .Env.USER_INFO_URI "https://services.humanbrainproject.eu/oidc/userinfo" }} revokeTokenUri: {{ default .Env.REVOKE_TOKEN_URI "https://services.humanbrainproject.eu/oidc/slo" }} @@ -52,6 +52,7 @@ logging: 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" }} diff --git a/pom.xml b/pom.xml index d206d2ad2..f0fbbe9dc 100644 --- a/pom.xml +++ b/pom.xml @@ -255,12 +255,6 @@ <artifactId>java-jwt</artifactId> <version>3.8.3</version> </dependency> - <dependency> - <groupId>org.keycloak</groupId> - <artifactId>keycloak-spring-boot-starter</artifactId> - </dependency> - - </dependencies> <build> diff --git a/src/main/java/eu/hbp/mip/configuration/KeycloakConfiguration.java b/src/main/java/eu/hbp/mip/configuration/KeycloakConfiguration.java deleted file mode 100644 index 45c598924..000000000 --- a/src/main/java/eu/hbp/mip/configuration/KeycloakConfiguration.java +++ /dev/null @@ -1,14 +0,0 @@ -package eu.hbp.mip.configuration; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import org.keycloak.KeycloakSecurityContext; - -public class KeycloakConfiguration { - - @Autowired - private HttpServletRequest request; - public KeycloakSecurityContext getKeycloakSecurityContext() { - return (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName()); - } -} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index ba6a380de..b8259bca6 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -8,6 +8,7 @@ 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 +35,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,223 +47,217 @@ 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; // 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. - */ -@KeycloakConfiguration -public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter { - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth.authenticationProvider(keycloakAuthenticationProvider()); - } +@Configuration +@EnableOAuth2Client +public class SecurityConfiguration extends WebSecurityConfigurerAdapter { - /** - * Defines the session authentication strategy. - */ - @Bean - @Override - protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { - return new NullAuthenticatedSessionStrategy(); - } + private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfiguration.class); - @Override - protected void configure(HttpSecurity http) throws Exception - { - super.configure(http); - http - .authorizeRequests() - .antMatchers("/user*").authenticated() - .antMatchers("/public1*").hasAuthority("public13") - .antMatchers("/public2*").hasAuthority("public2") - .antMatchers("/public3*").hasAuthority("public3") - .antMatchers("/admin*").authenticated() - .anyRequest().permitAll() - .and() - .logout() - .addLogoutHandler(keycloakLogoutHandler())//.logoutRequestMatcher(new AntPathRequestMatcher("/logout")) - .logoutUrl("/logout").permitAll() - .logoutSuccessUrl("/"); -// .anyRequest().permitAll().and().logout().addLogoutHandler(new KeycloakLogoutHandler(new RestTemplate())).logoutRequestMatcher(new AntPathRequestMatcher("/logout")); - ; - } + @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; -//@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; -// -//// @Autowired -//// private HttpServletRequest request; -// -// @Override -// protected void configure(HttpSecurity http) throws Exception { -// // @formatter:off -// http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class); -// -// if (authentication) { +// private HttpServletRequest request; + + @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() + .antMatchers("/galaxy*","/galaxy/*").hasRole("Data Manager") + //.anyRequest().authenticated() + .anyRequest().hasRole("Researcher") + .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 { + //keycloak + //KeycloakConfiguration.getKeycloakSecurityContext(); // 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 { -// //keycloak -// //KeycloakConfiguration.getKeycloakSecurityContext(); -//// 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 OAuth2ProtectedResourceDetails 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); -// } -// -// } -// } -//} +// .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); + } + + } + } + + @Bean + public AuthoritiesExtractor keycloakAuthoritiesExtractor() { + return new KeycloakAuthoritiesExtractor(); + } + + + public class KeycloakAuthoritiesExtractor + implements AuthoritiesExtractor { + + @Override + public List<GrantedAuthority> extractAuthorities + (Map<String, Object> map) { + return AuthorityUtils + .commaSeparatedStringToAuthorityList(asAuthorities(map)); + } + + private String asAuthorities(Map<String, Object> map) { + List<String> authorities = new ArrayList<>(); +// authorities.add("BAELDUNG_USER"); + List<LinkedHashMap<String, String>> authz; + authz = (List<LinkedHashMap<String, String>>) map.get("authorities"); + for (LinkedHashMap<String, String> entry : authz) { + authorities.add(entry.get("authority")); + } + return String.join(",", authorities); + } + } + +} diff --git a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java index cfddbd334..08c6f0410 100644 --- a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java +++ b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java @@ -17,6 +17,7 @@ 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 javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; @@ -102,6 +103,7 @@ 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()); -- GitLab From ca65f167f2039c44e1e66ef824d3481e80484b3f Mon Sep 17 00:00:00 2001 From: jerrypan44 <jerrypan44@gmail.com> Date: Fri, 29 Nov 2019 14:48:35 +0000 Subject: [PATCH 7/9] adding logging support for API calls --- pom.xml | 36 +-------- .../configuration/SecurityConfiguration.java | 3 +- .../eu/hbp/mip/controllers/ArticlesApi.java | 25 +++--- .../eu/hbp/mip/controllers/ExperimentApi.java | 81 +++++++++++-------- .../java/eu/hbp/mip/controllers/FilesAPI.java | 8 +- .../java/eu/hbp/mip/controllers/JWTApi.java | 5 +- .../eu/hbp/mip/controllers/MethodsApi.java | 8 +- .../eu/hbp/mip/controllers/MiningApi.java | 6 +- .../eu/hbp/mip/controllers/ModelsApi.java | 18 ++--- .../hbp/mip/controllers/PathologiesApi.java | 3 + .../eu/hbp/mip/controllers/SecurityApi.java | 14 ++-- .../java/eu/hbp/mip/controllers/StatsApi.java | 7 +- .../java/eu/hbp/mip/controllers/UsersApi.java | 5 +- .../eu/hbp/mip/utils/UserActionLogging.java | 23 ++++++ src/main/resources/logback.xml | 14 ++++ 15 files changed, 136 insertions(+), 120 deletions(-) create mode 100644 src/main/java/eu/hbp/mip/utils/UserActionLogging.java create mode 100644 src/main/resources/logback.xml diff --git a/pom.xml b/pom.xml index f0fbbe9dc..dfec021ef 100644 --- a/pom.xml +++ b/pom.xml @@ -66,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> @@ -99,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> @@ -146,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> diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index b8259bca6..9310be3de 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -4,6 +4,7 @@ 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; @@ -131,7 +132,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { private Filter ssoFilter() { OAuth2ClientAuthenticationProcessingFilter hbpFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/hbp"); OAuth2RestTemplate hbpTemplate = new OAuth2RestTemplate(hbp(), oauth2ClientContext); - hbpFilter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(frontendRedirectAfterLogin)); + hbpFilter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(frontendRedirectAfterLogin)); hbpFilter.setRestTemplate(hbpTemplate); hbpFilter.setTokenServices(new UserInfoTokenServices(hbpResource().getUserInfoUri(), hbp().getClientId())); return hbpFilter; diff --git a/src/main/java/eu/hbp/mip/controllers/ArticlesApi.java b/src/main/java/eu/hbp/mip/controllers/ArticlesApi.java index 3b567b102..963b18157 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 9f4fee480..deef20f75 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 d79717062..5acdb38ca 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 a3d939bbb..a1cb6afd5 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 fe7e8cd45..f88f67951 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 c289856a0..919fb5a2c 100644 --- a/src/main/java/eu/hbp/mip/controllers/MiningApi.java +++ b/src/main/java/eu/hbp/mip/controllers/MiningApi.java @@ -20,6 +20,7 @@ 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 java.util.*; import java.io.IOException; @@ -34,7 +35,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 @@ -46,7 +46,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"; @@ -64,7 +64,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"; diff --git a/src/main/java/eu/hbp/mip/controllers/ModelsApi.java b/src/main/java/eu/hbp/mip/controllers/ModelsApi.java index 56bc1ce10..02ee9ac6b 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 59511de85..27f635d32 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 08c6f0410..0f93c437e 100644 --- a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java +++ b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java @@ -18,6 +18,7 @@ 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; @@ -30,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 @@ -46,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")); @@ -54,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()) { @@ -77,6 +77,9 @@ public class SecurityApi { user.setAgreeNDA(agreeNDA); userRepository.save(user); } + + UserActionLogging.LogAction("user agreeNDA",""); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @@ -110,7 +113,8 @@ public class SecurityApi { 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 d09c032bc..a92adecde 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 89767017e..ed3104eb8 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 000000000..745fc24a6 --- /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 000000000..3843fbca8 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,14 @@ +<configuration> + <appender name="FILE1" class="ch.qos.logback.core.FileAppender"> + <file>logs/log1.txt</file> + <append>true</append> + <encoder> + <pattern>%msg%n</pattern> + </encoder> + </appender> + + + <logger name="eu.hbp.mip.utils" level="INFO" additivity="false"> + <appender-ref ref="FILE1" /> + </logger> +</configuration> \ No newline at end of file -- GitLab From 9b0503f3657a9dd29b1e6287ecb16353f4a48ee3 Mon Sep 17 00:00:00 2001 From: jerrypan44 <jerrypan44@gmail.com> Date: Mon, 9 Dec 2019 13:36:22 +0000 Subject: [PATCH 8/9] implementing logout flow in spring security --- docker/config/application.tmpl | 1 + .../configuration/SecurityConfiguration.java | 52 ++++++++++++++++++- src/main/resources/logback.xml | 6 ++- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index c05c497c1..0b8ee95bc 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -30,6 +30,7 @@ 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" }} + logoutUri: {{ default .Env.LOGOUT_URI }} tokenName: access_token authenticationScheme: query clientAuthenticationScheme: form diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index 9310be3de..675d5a2e0 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -53,6 +53,18 @@ 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; + + // 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 @@ -76,6 +88,12 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { */ @Value("#{'${frontend.loginUrl:/login/hbp}'}") private String loginUrl; + + /** + * Absolute URL to redirect to when logout is required + */ + @Value("#{'${hbp.client.logoutUri:http://88.197.53.10:8095/auth/realms/Demo/protocol/openid-connect/logout}'}") + private String logoutUri; /** * Absolute URL to redirect to after successful login @@ -94,6 +112,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { */ @Value("#{'${hbp.resource.revokeTokenUri:https://services.humanbrainproject.eu/oidc/revoke}'}") private String revokeTokenURI; + + // @Autowired // private HttpServletRequest request; @@ -114,7 +134,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { //.anyRequest().authenticated() .anyRequest().hasRole("Researcher") .and().exceptionHandling().authenticationEntryPoint(new CustomLoginUrlAuthenticationEntryPoint(loginUrl)) - .and().logout().addLogoutHandler(new CustomLogoutHandler()).logoutSuccessUrl(redirectAfterLogoutUrl) + .and().logout().addLogoutHandler(authLogoutHandler()).logoutSuccessUrl(redirectAfterLogoutUrl) .and().logout().permitAll() .and().csrf().ignoringAntMatchers("/logout").csrfTokenRepository(csrfTokenRepository()) .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class) @@ -260,5 +280,35 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { 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", registration.getClientSecret()); + formParams.add("refresh_token", this.oauth2ClientContext.getAccessToken().getRefreshToken().getValue()); + // ãƒªã‚¯ã‚¨ã‚¹ãƒˆãƒ˜ãƒƒãƒ€ãƒ¼ã‚’ä½œæˆ + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE); + // ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’ä½œæˆ + RequestEntity<MultiValueMap<String, String>> requestEntity = + new RequestEntity<>(formParams, httpHeaders, HttpMethod.POST, + //URI.create("http://88.197.53.10:8095/auth/realms/Demo/protocol/openid-connect/logout")); //todo make this parameter + URI.create(logoutUri)); //todo make this parameter + // POSTリクエストé€ä¿¡ï¼ˆãƒã‚°ã‚¢ã‚¦ãƒˆå®Ÿè¡Œï¼‰ + + ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity, String.class); + } + } diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 3843fbca8..d1875652c 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -3,11 +3,13 @@ <file>logs/log1.txt</file> <append>true</append> <encoder> - <pattern>%msg%n</pattern> + <pattern>%d{yyyy-MM-dd} %msg%n</pattern> </encoder> </appender> - + <logger name="org.springframework"> + <appender-ref ref="FILE1" /> + </logger> <logger name="eu.hbp.mip.utils" level="INFO" additivity="false"> <appender-ref ref="FILE1" /> </logger> -- GitLab From 517613360e7a4b3e4f99e42ba55b711c6ab01212 Mon Sep 17 00:00:00 2001 From: jerry <Jerrypan44@gmail.com> Date: Fri, 27 Dec 2019 12:52:02 +0000 Subject: [PATCH 9/9] disabling certificate validation for parametrized IP address --- docker/config/application.tmpl | 5 +- src/main/java/eu/hbp/mip/MIPApplication.java | 9 +++ .../configuration/SecurityConfiguration.java | 61 ++++++++++++++++++- src/main/resources/logback.xml | 5 +- 4 files changed, 76 insertions(+), 4 deletions(-) diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index 0b8ee95bc..0e5fc5706 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -47,7 +47,7 @@ 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" }} @@ -88,4 +88,5 @@ services: galaxyUsername: {{ default .Env.GALAXY_USERNAME "admin" }} galaxyPassword: {{ default .Env.GALAXY_PASSWORD "password" }} galaxyContext: {{ default .Env.GALAXY_CONTEXT "nativeGalaxy" }} - + keycloak: + keycloakUrl: {{ default .Env.KEYCLOAK_URL "88.197.53.10"}} diff --git a/src/main/java/eu/hbp/mip/MIPApplication.java b/src/main/java/eu/hbp/mip/MIPApplication.java index df70ba78a..befb16ec9 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 675d5a2e0..bf17e5564 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -65,6 +65,18 @@ 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 @@ -120,6 +132,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { + disableCertificateValidation(); // @formatter:off http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class); @@ -309,6 +322,52 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity, String.class); } - + @Value("#{'${services.keycloak.keycloakUrl:88.197.53.10}'}") + private String keycloakUrl; + + // static { + // disableCertificateValidation(); + // } + + 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) {} + } }; + + + // Ignore differences between given hostname and certificate hostname + HostnameVerifier hv = new HostnameVerifier() { + public boolean verify(String hostname, SSLSession session) { + + // System.out.println("Warning: URL Host: " + hostname + " vs. " + // + session.getPeerHost()); + if(hostname.equals(keycloakUrl) && session.getPeerHost().equals(keycloakUrl)) + { + return true; + } + else + { + return false; + } + } + }; + + // 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/resources/logback.xml b/src/main/resources/logback.xml index d1875652c..efd7a874a 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -3,10 +3,13 @@ <file>logs/log1.txt</file> <append>true</append> <encoder> - <pattern>%d{yyyy-MM-dd} %msg%n</pattern> + <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> -- GitLab