diff --git a/.bumpversion.cfg b/.bumpversion.cfg index be9e8543f1b621735a8cb85b9b2e94dd0bca564f..a0c10f8e63abfd3df157ed44d700aa9cceea78b6 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.8.0 +current_version = 2.8.1 commit = True tag = True tag_name = {new_version} diff --git a/hbp.yml b/hbp.yml index 24cb8215e61c5f17878f8681bbc08f8b360d46dd..02066a86c0d140fed69d9ea6531cec471db509b7 100644 --- a/hbp.yml +++ b/hbp.yml @@ -58,9 +58,9 @@ testing: command: ./test.sh release_management: - current_version: 2.8.0 - current_code_release: https://github.com/HBPMedical/portal-backend/archive/2.8.0.zip - current_binary_release: https://pypi.python.org/pypi/portal-backend/2.8.0 + current_version: 2.8.1 + current_code_release: https://github.com/HBPMedical/portal-backend/archive/2.8.1.zip + current_binary_release: https://pypi.python.org/pypi/portal-backend/2.8.1 release_script: 'publish.sh' continuous_integration: @@ -75,10 +75,10 @@ continuous_integration: distribution: docker_hub: name: hbpmip/portal-backend - current_tag: 2.8.0 + current_tag: 2.8.1 url: https://hub.docker.com/r/hbpmip/portal-backend/ badge: https://img.shields.io/badge/docker-hbpmip%2Fportal--backend-008bb8.svg - command: docker pull hbpmip/portal-backend:2.8.0 + command: docker pull hbpmip/portal-backend:2.8.1 planning: github: diff --git a/pom.xml b/pom.xml index ef5d703da8dfa454a714eafbd377bfbddd37a5d1..965b0c07a7ef5332bd378e9268413dbc7f4d9027 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ <groupId>eu.hbp.mip</groupId> <artifactId>portal-backend</artifactId> - <version>2.8.0</version><!-- BUMP_VERSION --> + <version>2.8.1</version><!-- BUMP_VERSION --> <packaging>jar</packaging> <name>portal-backend</name> diff --git a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java index 89f5133a835817f11da8be3f191c4726351bc4aa..d8ec1acfbc932f8cb5faa1a42199904ffc88a092 100644 --- a/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/SecurityConfiguration.java @@ -1,28 +1,19 @@ package eu.hbp.mip.configuration; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.gson.Gson; -import eu.hbp.mip.model.User; import eu.hbp.mip.model.UserInfo; -import eu.hbp.mip.repositories.UserRepository; import eu.hbp.mip.utils.CORSFilter; import eu.hbp.mip.utils.CustomLoginUrlAuthenticationEntryPoint; import eu.hbp.mip.utils.HTTPUtil; -import io.swagger.annotations.ApiParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices; import org.springframework.boot.context.embedded.FilterRegistrationBean; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.Authentication; @@ -41,10 +32,6 @@ import org.springframework.security.web.csrf.CsrfFilter; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.csrf.CsrfTokenRepository; import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.WebUtils; @@ -55,9 +42,6 @@ import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.security.Principal; // 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 @@ -67,7 +51,6 @@ import java.security.Principal; */ @Configuration @EnableOAuth2Client -@RestController public class SecurityConfiguration extends WebSecurityConfigurerAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfiguration.class); @@ -75,12 +58,6 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private OAuth2ClientContext oauth2ClientContext; - @Autowired - private UserRepository userRepository; - - @Autowired - private UserInfo userInfo; - /** * Enable HBP collab authentication (1) or disable it (0). Default is 1 */ @@ -111,11 +88,6 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Value("#{'${hbp.resource.revokeTokenUri:https://services.humanbrainproject.eu/oidc/revoke}'}") private String revokeTokenURI; - /** - * Set to true if using no-auth mode and user has clicked on the login button - */ - private boolean fakeAuth = false; - @Override protected void configure(HttpSecurity http) throws Exception { // @formatter:off @@ -139,8 +111,6 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { http.antMatcher("/**") .authorizeRequests() .antMatchers("/**").permitAll().and().csrf().disable(); - User user = userInfo.getUser(); - userRepository.save(user); } } @@ -174,6 +144,14 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { return new ResourceServerProperties(); } + public boolean isAuthentication() { + return authentication; + } + + public String getFrontendRedirectAfterLogin() { + return frontendRedirectAfterLogin; + } + private Filter csrfHeaderFilter() { return new OncePerRequestFilter() { @Override @@ -200,56 +178,15 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { return repository; } - @RequestMapping(path = "/user", method = RequestMethod.GET) - public Object user(Principal principal, HttpServletResponse response) { - ObjectMapper mapper = new ObjectMapper(); - - try { - String userJSON = mapper.writeValueAsString(userInfo.getUser()); - Cookie cookie = new Cookie("user", URLEncoder.encode(userJSON, "UTF-8")); - cookie.setSecure(true); - cookie.setPath("/"); - response.addCookie(cookie); - } catch (JsonProcessingException | UnsupportedEncodingException e) { - LOGGER.trace("Cannot read user json", e); - } - - if(!authentication) - { - if(!fakeAuth) - { - response.setStatus(401); - } - String principalJson = "{\"principal\": \"anonymous\", \"name\": \"anonymous\", \"userAuthentication\": {" + - "\"details\": {\"preferred_username\": \"anonymous\"}}}"; - return new Gson().fromJson(principalJson, Object.class); - } - - return principal; - } - - @RequestMapping(path = "/user", method = RequestMethod.POST) - public ResponseEntity<Void> postUser(@ApiParam(value = "Has the user agreed on the NDA") @RequestParam(value = "agreeNDA") Boolean agreeNDA) { - User user = userInfo.getUser(); - if (user != null) { - user.setAgreeNDA(agreeNDA); - userRepository.save(user); - } - return new ResponseEntity<>(HttpStatus.NO_CONTENT); - } - - @RequestMapping(path = "/login/hbp", method = RequestMethod.GET) - @ConditionalOnExpression("${hbp.authentication.enabled:0}") - public void noLogin(HttpServletResponse httpServletResponse) throws IOException { - fakeAuth = true; - httpServletResponse.sendRedirect(frontendRedirectAfterLogin); - } - private class CustomLogoutHandler implements LogoutHandler { @Override public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) { - fakeAuth = false; + // 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) { diff --git a/src/main/java/eu/hbp/mip/controllers/SecurityApi.java b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java new file mode 100644 index 0000000000000000000000000000000000000000..a37c9e87a3dd2b5432a98d9f917ed779a54435e6 --- /dev/null +++ b/src/main/java/eu/hbp/mip/controllers/SecurityApi.java @@ -0,0 +1,86 @@ +package eu.hbp.mip.controllers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import eu.hbp.mip.configuration.SecurityConfiguration; +import eu.hbp.mip.model.User; +import eu.hbp.mip.model.UserInfo; +import eu.hbp.mip.repositories.UserRepository; +import io.swagger.annotations.ApiParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.security.Principal; + +@RestController +public class SecurityApi { + + private static final Logger LOGGER = LoggerFactory.getLogger(SecurityApi.class); + + @Autowired + private UserInfo userInfo; + + @Autowired + private UserRepository userRepository; + + @Autowired + private SecurityConfiguration securityConfiguration; + + @RequestMapping(path = "/user", method = RequestMethod.GET) + public Object user(Principal principal, HttpServletResponse response) { + ObjectMapper mapper = new ObjectMapper(); + + try { + String userJSON = mapper.writeValueAsString(userInfo.getUser()); + Cookie cookie = new Cookie("user", URLEncoder.encode(userJSON, "UTF-8")); + cookie.setSecure(true); + cookie.setPath("/"); + response.addCookie(cookie); + } catch (JsonProcessingException | UnsupportedEncodingException e) { + LOGGER.trace("Cannot read user json", e); + } + + if (!securityConfiguration.isAuthentication()) { + if (!userInfo.isFakeAuth()) { + response.setStatus(401); + } + String principalJson = "{\"principal\": \"anonymous\", \"name\": \"anonymous\", \"userAuthentication\": {" + + "\"details\": {\"preferred_username\": \"anonymous\"}}}"; + return new Gson().fromJson(principalJson, Object.class); + } + + return principal; + } + + @RequestMapping(path = "/user", method = RequestMethod.POST) + public ResponseEntity<Void> postUser(@ApiParam(value = "Has the user agreed on the NDA") @RequestParam(value = "agreeNDA") Boolean agreeNDA) { + User user = userInfo.getUser(); + if (user != null) { + user.setAgreeNDA(agreeNDA); + userRepository.save(user); + } + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @RequestMapping(path = "/login/hbp", method = RequestMethod.GET) + @ConditionalOnExpression("${hbp.authentication.enabled:0}") + public void noLogin(HttpServletResponse httpServletResponse) throws IOException { + userInfo.setFakeAuth(true); + httpServletResponse.sendRedirect(securityConfiguration.getFrontendRedirectAfterLogin()); + } + +} diff --git a/src/main/java/eu/hbp/mip/model/UserInfo.java b/src/main/java/eu/hbp/mip/model/UserInfo.java index 3bc14553b56f3c1538cc6669a8605dbe008bb4e4..f4671b634d152244ed79976ffe2a170b9130492e 100644 --- a/src/main/java/eu/hbp/mip/model/UserInfo.java +++ b/src/main/java/eu/hbp/mip/model/UserInfo.java @@ -4,13 +4,17 @@ import eu.hbp.mip.repositories.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.stereotype.Component; +import javax.inject.Named; + @Component -@Scope("session") +@Scope(value = "session", proxyMode=ScopedProxyMode.TARGET_CLASS) +@Named("userInfo") public class UserInfo { @Autowired @@ -24,6 +28,11 @@ public class UserInfo { private User user; + /** + * Set to true if using no-auth mode and user has clicked on the login button + */ + private boolean fakeAuth = false; + /** * returns the user for the current session. * <p> @@ -57,6 +66,14 @@ public class UserInfo { return user; } + public boolean isFakeAuth() { + return fakeAuth; + } + + public void setFakeAuth(boolean fakeAuth) { + this.fakeAuth = fakeAuth; + } + private String getUserInfos() { OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication(); Authentication userAuthentication = oAuth2Authentication.getUserAuthentication();