From b0b5876981a92b71e9c2fd5e0f2ebae71e01c0f6 Mon Sep 17 00:00:00 2001 From: anhefti Date: Mon, 26 Nov 2018 13:47:08 +0100 Subject: [PATCH] SEBSERV-4 #Unauthorized request redirect to login or respond with error --- .../ClientSessionWebSecurityConfig.java | 81 ++++++++++++++++++- .../weblayer/CustomAuthenticationError.java | 35 -------- .../oauth/WebResourceServerConfiguration.java | 9 +++ 3 files changed, 89 insertions(+), 36 deletions(-) delete mode 100644 src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/CustomAuthenticationError.java diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/ClientSessionWebSecurityConfig.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/ClientSessionWebSecurityConfig.java index 964d5103..cc40cad5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/ClientSessionWebSecurityConfig.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/ClientSessionWebSecurityConfig.java @@ -8,16 +8,29 @@ package ch.ethz.seb.sebserver.webservice.weblayer; +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.http.MediaType; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.AuthenticationException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration; import org.springframework.security.oauth2.provider.token.AccessTokenConverter; @@ -25,19 +38,37 @@ import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConv import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter; +import org.springframework.security.web.AuthenticationEntryPoint; import ch.ethz.seb.sebserver.WebSecurityConfig; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.webservice.weblayer.oauth.WebClientDetailsService; import ch.ethz.seb.sebserver.webservice.weblayer.oauth.WebResourceServerConfiguration; -/** This is the main web-security Spring configuration for SEB-Server webservice API */ +/** This is the main web-security Spring configuration for SEB-Server webservice API + * + * Currently two separated Rest API's are implemented, one for administration and maintenance + * of the SEB-Server (AdminAPI) and one for SEB-Client connection on running exams and eventually + * also for LMS communication), if needed (ExamAPI). The AdministrationAPI uses OAuth 2 password + * grant with refresh-token, same as in the prototype and the ExamAPI uses the client_credential grant. + * + * There is a Spring Authorization-Server defining this two clients (AdminAPIClient and ExamAPIClient) as well as + * two Spring Resource-Server for the separation of the different API's + * + * The endpoint of the AdministrationAPI can be configured within the key; sebserver.webservice.api.admin.endpoint + * and is by default set to "/admin-api/**" + * + * The endpoint of the ExamAPI can be configured within the key; sebserver.webservice.api.exam.endpoint + * and is by default set to "/exam-api/**" */ @WebServiceProfile @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) @EnableWebSecurity +@Order(4) public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter { + private static final Logger log = LoggerFactory.getLogger(ClientSessionWebSecurityConfig.class); + /** Spring bean name of single AuthenticationManager bean */ public static final String AUTHENTICATION_MANAGER = "AUTHENTICATION_MANAGER"; @@ -103,6 +134,28 @@ public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter this.examAPIEndpoint); } + @Override + public void configure(final HttpSecurity http) throws Exception { + http + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .antMatcher("/**") + .authorizeRequests() + .anyRequest() + .authenticated() + .and() + .exceptionHandling() + .authenticationEntryPoint(new LoginRedirectOnUnauthorized()) + .and() + .formLogin().disable() + .httpBasic().disable() + .logout().disable() + .headers().frameOptions().disable() + .and() + .csrf().disable(); + } + // NOTE: We need two different class types here to support Spring configuration for different // ResourceServerConfiguration. There is a class type now for the Admin API as well as for the Exam API private static final class AdminAPIResourceServerConfiguration extends WebResourceServerConfiguration { @@ -117,6 +170,7 @@ public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter tokenStore, webServiceClientDetails, authenticationManager, + new LoginRedirectOnUnauthorized(), ADMIN_API_RESOURCE_ID, apiEndpoint, true, @@ -138,6 +192,13 @@ public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter tokenStore, webServiceClientDetails, authenticationManager, + (request, response, exception) -> { + response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + log.warn("Unauthorized Request: {}", request, exception); + log.info("Redirect to login after unauthorized request"); + response.getOutputStream().println("{ \"error\": \"" + exception.getMessage() + "\" }"); + }, EXAM_API_RESOURCE_ID, apiEndpoint, true, @@ -145,4 +206,22 @@ public class ClientSessionWebSecurityConfig extends WebSecurityConfigurerAdapter } } + private static class LoginRedirectOnUnauthorized implements AuthenticationEntryPoint { + + @Override + public void commence( + final HttpServletRequest request, + final HttpServletResponse response, + final AuthenticationException authenticationException) throws IOException, ServletException { + + response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + log.warn("Unauthorized Request: {}", request, authenticationException); + log.info("Redirect to login after unauthorized request"); + + // TODO define login redirect + response.sendRedirect("/gui/"); + } + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/CustomAuthenticationError.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/CustomAuthenticationError.java deleted file mode 100644 index 6bc72050..00000000 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/CustomAuthenticationError.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET) - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package ch.ethz.seb.sebserver.webservice.weblayer; - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.http.MediaType; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.stereotype.Component; - -/** Implements a custom unauthorized authentication error end-point */ -@Component -public class CustomAuthenticationError implements AuthenticationEntryPoint { - - @Override - public void commence(final HttpServletRequest request, final HttpServletResponse response, - final AuthenticationException authenticationException) throws IOException, ServletException { - - response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - response.getOutputStream().println("{ \"error\": \"" + authenticationException.getMessage() + "\" }"); - - } -} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebResourceServerConfiguration.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebResourceServerConfiguration.java index ef7784ee..dff77ef6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebResourceServerConfiguration.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebResourceServerConfiguration.java @@ -20,6 +20,7 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.R import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.DefaultTokenServices; import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.security.web.AuthenticationEntryPoint; /** Abstract Spring ResourceServerConfiguration to configure different resource services * for different API's. */ @@ -34,6 +35,7 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi final TokenStore tokenStore, final WebClientDetailsService webServiceClientDetails, final AuthenticationManager authenticationManager, + final AuthenticationEntryPoint authenticationEntryPoint, final String resourceId, final String apiEndpoint, final boolean supportRefreshToken, @@ -44,6 +46,7 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi tokenStore, webServiceClientDetails, authenticationManager, + authenticationEntryPoint, resourceId, apiEndpoint, supportRefreshToken); @@ -62,6 +65,7 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi private final TokenStore tokenStore; private final WebClientDetailsService webServiceClientDetails; private final AuthenticationManager authenticationManager; + private final AuthenticationEntryPoint authenticationEntryPoint; private final String resourceId; private final String apiEndpoint; private final boolean supportRefreshToken; @@ -70,6 +74,7 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi final TokenStore tokenStore, final WebClientDetailsService webServiceClientDetails, final AuthenticationManager authenticationManager, + final AuthenticationEntryPoint authenticationEntryPoint, final String resourceId, final String apiEndpoint, final boolean supportRefreshToken) { @@ -78,6 +83,7 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi this.tokenStore = tokenStore; this.webServiceClientDetails = webServiceClientDetails; this.authenticationManager = authenticationManager; + this.authenticationEntryPoint = authenticationEntryPoint; this.resourceId = resourceId; this.apiEndpoint = apiEndpoint; this.supportRefreshToken = supportRefreshToken; @@ -105,6 +111,9 @@ public abstract class WebResourceServerConfiguration extends ResourceServerConfi .anyRequest() .authenticated() .and() + .exceptionHandling() + .authenticationEntryPoint(this.authenticationEntryPoint) + .and() .formLogin().disable() .httpBasic().disable() .logout().disable()