Allow previously authorized users to access the error page
Prior to this commit, the `ErrorPageSecurityFilter` verified if access to the error page was allowed by invoking the `WebInvocationPrivilegeEvaluator` with the Authentication from the `SecurityContextHolder`. This meant that access to the error page was denied for a `null` Authentication or `AnonymousAuthenticationToken` in cases where the error page required authenticated access. This prevented authorized users from accessing the error page in case the Authentication wasn't retrievable for the error dispatch, which is the case for `@Transient` authentication or stateless session policy. This commit updates the `ErrorPageSecurityFilter` to check access to the error page only if the error is an authn or authz error in cases where an authentication object is not found in the SecurityContextHolder. This makes the error response consistent when bad credentials or no credentials are used while also allowing access to previously authorized users. Fixes gh-28953
This commit is contained in:
parent
c077ebecf7
commit
d9d161cd6b
|
@ -31,6 +31,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||
|
@ -67,17 +68,28 @@ public class ErrorPageSecurityFilter implements Filter {
|
|||
|
||||
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
if (DispatcherType.ERROR.equals(request.getDispatcherType()) && !isAllowed(request)) {
|
||||
sendError(request, response);
|
||||
Integer errorCode = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
|
||||
if (DispatcherType.ERROR.equals(request.getDispatcherType()) && !isAllowed(request, errorCode)) {
|
||||
response.sendError((errorCode != null) ? errorCode : 401);
|
||||
return;
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private boolean isAllowed(HttpServletRequest request) {
|
||||
String uri = request.getRequestURI();
|
||||
private boolean isAllowed(HttpServletRequest request, Integer errorCode) {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
return getPrivilegeEvaluator().isAllowed(uri, authentication);
|
||||
if (isUnauthenticated(authentication) && isNotAuthenticationError(errorCode)) {
|
||||
return true;
|
||||
}
|
||||
return getPrivilegeEvaluator().isAllowed(request.getRequestURI(), authentication);
|
||||
}
|
||||
|
||||
private boolean isUnauthenticated(Authentication authentication) {
|
||||
return (authentication == null || authentication instanceof AnonymousAuthenticationToken);
|
||||
}
|
||||
|
||||
private boolean isNotAuthenticationError(Integer errorCode) {
|
||||
return (errorCode == null || (errorCode != 401 && errorCode != 403));
|
||||
}
|
||||
|
||||
private WebInvocationPrivilegeEvaluator getPrivilegeEvaluator() {
|
||||
|
@ -98,11 +110,6 @@ public class ErrorPageSecurityFilter implements Filter {
|
|||
}
|
||||
}
|
||||
|
||||
private void sendError(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
Integer errorCode = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
|
||||
response.sendError((errorCode != null) ? errorCode : 401);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link WebInvocationPrivilegeEvaluator} that always allows access.
|
||||
*/
|
||||
|
|
|
@ -20,6 +20,7 @@ import javax.servlet.DispatcherType;
|
|||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -27,6 +28,9 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -64,6 +68,11 @@ class ErrorPageSecurityFilterTests {
|
|||
this.securityFilter = new ErrorPageSecurityFilter(this.context);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenAccessIsAllowedShouldContinueDownFilterChain() throws Exception {
|
||||
given(this.privilegeEvaluator.isAllowed(anyString(), any())).willReturn(true);
|
||||
|
@ -83,6 +92,9 @@ class ErrorPageSecurityFilterTests {
|
|||
@Test
|
||||
void whenAccessIsDeniedAndNoErrorCodeAttributeOnRequest() throws Exception {
|
||||
given(this.privilegeEvaluator.isAllowed(anyString(), any())).willReturn(false);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
SecurityContextHolder.setContext(securityContext);
|
||||
given(securityContext.getAuthentication()).willReturn(mock(Authentication.class));
|
||||
this.securityFilter.doFilter(this.request, this.response, this.filterChain);
|
||||
verifyNoInteractions(this.filterChain);
|
||||
assertThat(this.response.getStatus()).isEqualTo(401);
|
||||
|
|
|
@ -66,7 +66,8 @@ class SampleActuatorCustomSecurityApplicationTests extends AbstractSampleActuato
|
|||
void testInsecureApplicationPath() {
|
||||
ResponseEntity<Map> entity = restTemplate().getForEntity(getPath() + "/foo", Map.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
assertThat(entity.getBody()).isNull();
|
||||
Map<String, Object> body = entity.getBody();
|
||||
assertThat((String) body.get("message")).contains("Expected exception in controller");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -18,10 +18,6 @@ package smoketest.web.secure;
|
|||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
|
@ -38,21 +34,4 @@ public class SampleWebSecureApplication implements WebMvcConfigurer {
|
|||
new SpringApplicationBuilder(SampleWebSecureApplication.class).run(args);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
protected static class ApplicationSecurity {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain configure(HttpSecurity http) throws Exception {
|
||||
http.csrf().disable();
|
||||
http.authorizeRequests((requests) -> {
|
||||
requests.antMatchers("/public/**").permitAll();
|
||||
requests.anyRequest().fullyAuthenticated();
|
||||
});
|
||||
http.httpBasic();
|
||||
http.formLogin((form) -> form.loginPage("/login").permitAll());
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package smoketest.web.secure;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Abstract base class for tests to ensure that the error page is accessible only to
|
||||
* authorized users.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
abstract class AbstractErrorPageTests {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate testRestTemplate;
|
||||
|
||||
@Test
|
||||
void testBadCredentials() {
|
||||
final ResponseEntity<JsonNode> response = this.testRestTemplate.withBasicAuth("username", "wrongpassword")
|
||||
.exchange("/test", HttpMethod.GET, null, JsonNode.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
JsonNode jsonResponse = response.getBody();
|
||||
assertThat(jsonResponse).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNoCredentials() {
|
||||
final ResponseEntity<JsonNode> response = this.testRestTemplate.exchange("/test", HttpMethod.GET, null,
|
||||
JsonNode.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
JsonNode jsonResponse = response.getBody();
|
||||
assertThat(jsonResponse).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPublicNotFoundPage() {
|
||||
final ResponseEntity<JsonNode> response = this.testRestTemplate.exchange("/public/notfound", HttpMethod.GET,
|
||||
null, JsonNode.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
JsonNode jsonResponse = response.getBody();
|
||||
assertThat(jsonResponse.get("error").asText()).isEqualTo("Not Found");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPublicNotFoundPageWithCorrectCredentials() {
|
||||
final ResponseEntity<JsonNode> response = this.testRestTemplate.withBasicAuth("username", "password")
|
||||
.exchange("/public/notfound", HttpMethod.GET, null, JsonNode.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
JsonNode jsonResponse = response.getBody();
|
||||
assertThat(jsonResponse.get("error").asText()).isEqualTo("Not Found");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPublicNotFoundPageWithBadCredentials() {
|
||||
final ResponseEntity<JsonNode> response = this.testRestTemplate.withBasicAuth("username", "wrong")
|
||||
.exchange("/public/notfound", HttpMethod.GET, null, JsonNode.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
JsonNode jsonResponse = response.getBody();
|
||||
assertThat(jsonResponse).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCorrectCredentialsWithControllerException() {
|
||||
final ResponseEntity<JsonNode> response = this.testRestTemplate.withBasicAuth("username", "password")
|
||||
.exchange("/fail", HttpMethod.GET, null, JsonNode.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
JsonNode jsonResponse = response.getBody();
|
||||
assertThat(jsonResponse.get("error").asText()).isEqualTo("Internal Server Error");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCorrectCredentials() {
|
||||
final ResponseEntity<String> response = this.testRestTemplate.withBasicAuth("username", "password")
|
||||
.exchange("/test", HttpMethod.GET, null, String.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
response.getBody();
|
||||
assertThat(response.getBody()).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class TestConfiguration {
|
||||
|
||||
@RestController
|
||||
static class TestController {
|
||||
|
||||
@GetMapping("/test")
|
||||
String test() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
@GetMapping("/fail")
|
||||
String fail() {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -16,21 +16,11 @@
|
|||
|
||||
package smoketest.web.secure;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
/**
|
||||
* Tests to ensure that the error page is accessible only to authorized users.
|
||||
|
@ -38,61 +28,24 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
* @author Madhura Bhave
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
classes = { ErrorPageTests.TestConfiguration.class, SampleWebSecureApplication.class },
|
||||
classes = { AbstractErrorPageTests.TestConfiguration.class, ErrorPageTests.SecurityConfiguration.class,
|
||||
SampleWebSecureApplication.class },
|
||||
properties = { "server.error.include-message=always", "spring.security.user.name=username",
|
||||
"spring.security.user.password=password" })
|
||||
class ErrorPageTests {
|
||||
class ErrorPageTests extends AbstractErrorPageTests {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate testRestTemplate;
|
||||
|
||||
@Test
|
||||
void testBadCredentials() {
|
||||
final ResponseEntity<JsonNode> response = this.testRestTemplate.withBasicAuth("username", "wrongpassword")
|
||||
.exchange("/test", HttpMethod.GET, null, JsonNode.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
JsonNode jsonResponse = response.getBody();
|
||||
assertThat(jsonResponse).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNoCredentials() {
|
||||
final ResponseEntity<JsonNode> response = this.testRestTemplate.exchange("/test", HttpMethod.GET, null,
|
||||
JsonNode.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
JsonNode jsonResponse = response.getBody();
|
||||
assertThat(jsonResponse).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPublicNotFoundPage() {
|
||||
final ResponseEntity<JsonNode> response = this.testRestTemplate.exchange("/public/notfound", HttpMethod.GET,
|
||||
null, JsonNode.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
JsonNode jsonResponse = response.getBody();
|
||||
assertThat(jsonResponse).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCorrectCredentials() {
|
||||
final ResponseEntity<String> response = this.testRestTemplate.withBasicAuth("username", "password")
|
||||
.exchange("/test", HttpMethod.GET, null, String.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
response.getBody();
|
||||
assertThat(response.getBody()).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class TestConfiguration {
|
||||
|
||||
@RestController
|
||||
static class TestController {
|
||||
|
||||
@GetMapping("/test")
|
||||
String test() {
|
||||
return "test";
|
||||
}
|
||||
@org.springframework.boot.test.context.TestConfiguration(proxyBeanMethods = false)
|
||||
static class SecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain configure(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests((requests) -> {
|
||||
requests.antMatchers("/public/**").permitAll();
|
||||
requests.anyRequest().fullyAuthenticated();
|
||||
});
|
||||
http.httpBasic();
|
||||
http.formLogin((form) -> form.loginPage("/login").permitAll());
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package smoketest.web.secure;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
/**
|
||||
* Tests for error page when a stateless session creation policy is used.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
classes = { AbstractErrorPageTests.TestConfiguration.class, NoSessionErrorPageTests.SecurityConfiguration.class,
|
||||
SampleWebSecureApplication.class },
|
||||
properties = { "server.error.include-message=always", "spring.security.user.name=username",
|
||||
"spring.security.user.password=password" })
|
||||
class NoSessionErrorPageTests extends AbstractErrorPageTests {
|
||||
|
||||
@org.springframework.boot.test.context.TestConfiguration(proxyBeanMethods = false)
|
||||
static class SecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||
http.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
.authorizeRequests((requests) -> {
|
||||
requests.antMatchers("/public/**").permitAll();
|
||||
requests.anyRequest().authenticated();
|
||||
});
|
||||
http.httpBasic();
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -25,12 +25,15 @@ import org.springframework.boot.test.context.SpringBootTest;
|
|||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
|
@ -42,7 +45,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
* @author Dave Syer
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
classes = { SampleWebSecureApplicationTests.SecurityConfiguration.class, SampleWebSecureApplication.class })
|
||||
class SampleWebSecureApplicationTests {
|
||||
|
||||
@Autowired
|
||||
|
@ -85,4 +89,21 @@ class SampleWebSecureApplicationTests {
|
|||
assertThat(entity.getHeaders().getLocation().toString()).endsWith(this.port + "/");
|
||||
}
|
||||
|
||||
@org.springframework.boot.test.context.TestConfiguration(proxyBeanMethods = false)
|
||||
static class SecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain configure(HttpSecurity http) throws Exception {
|
||||
http.csrf().disable();
|
||||
http.authorizeRequests((requests) -> {
|
||||
requests.antMatchers("/public/**").permitAll();
|
||||
requests.anyRequest().fullyAuthenticated();
|
||||
});
|
||||
http.httpBasic();
|
||||
http.formLogin((form) -> form.loginPage("/login").permitAll());
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package smoketest.web.secure;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for error page that permits access to all.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
classes = { AbstractErrorPageTests.TestConfiguration.class,
|
||||
UnauthenticatedErrorPageTests.SecurityConfiguration.class, SampleWebSecureApplication.class },
|
||||
properties = { "server.error.include-message=always", "spring.security.user.name=username",
|
||||
"spring.security.user.password=password" })
|
||||
class UnauthenticatedErrorPageTests {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate testRestTemplate;
|
||||
|
||||
@Test
|
||||
void testBadCredentials() {
|
||||
final ResponseEntity<JsonNode> response = this.testRestTemplate.withBasicAuth("username", "wrongpassword")
|
||||
.exchange("/test", HttpMethod.GET, null, JsonNode.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
JsonNode jsonResponse = response.getBody();
|
||||
assertThat(jsonResponse.get("error").asText()).isEqualTo("Unauthorized");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNoCredentials() {
|
||||
final ResponseEntity<JsonNode> response = this.testRestTemplate.exchange("/test", HttpMethod.GET, null,
|
||||
JsonNode.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
JsonNode jsonResponse = response.getBody();
|
||||
assertThat(jsonResponse.get("error").asText()).isEqualTo("Unauthorized");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPublicNotFoundPage() {
|
||||
final ResponseEntity<JsonNode> response = this.testRestTemplate.exchange("/public/notfound", HttpMethod.GET,
|
||||
null, JsonNode.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
JsonNode jsonResponse = response.getBody();
|
||||
assertThat(jsonResponse.get("error").asText()).isEqualTo("Not Found");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPublicNotFoundPageWithCorrectCredentials() {
|
||||
final ResponseEntity<JsonNode> response = this.testRestTemplate.withBasicAuth("username", "password")
|
||||
.exchange("/public/notfound", HttpMethod.GET, null, JsonNode.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
JsonNode jsonResponse = response.getBody();
|
||||
assertThat(jsonResponse.get("error").asText()).isEqualTo("Not Found");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPublicNotFoundPageWithBadCredentials() {
|
||||
final ResponseEntity<JsonNode> response = this.testRestTemplate.withBasicAuth("username", "wrong")
|
||||
.exchange("/public/notfound", HttpMethod.GET, null, JsonNode.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
JsonNode jsonResponse = response.getBody();
|
||||
assertThat(jsonResponse.get("error").asText()).isEqualTo("Unauthorized");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCorrectCredentialsWithControllerException() {
|
||||
final ResponseEntity<JsonNode> response = this.testRestTemplate.withBasicAuth("username", "password")
|
||||
.exchange("/fail", HttpMethod.GET, null, JsonNode.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
JsonNode jsonResponse = response.getBody();
|
||||
assertThat(jsonResponse.get("error").asText()).isEqualTo("Internal Server Error");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCorrectCredentials() {
|
||||
final ResponseEntity<String> response = this.testRestTemplate.withBasicAuth("username", "password")
|
||||
.exchange("/test", HttpMethod.GET, null, String.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isEqualTo("test");
|
||||
}
|
||||
|
||||
@org.springframework.boot.test.context.TestConfiguration(proxyBeanMethods = false)
|
||||
static class SecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests((requests) -> {
|
||||
requests.antMatchers("/error").permitAll();
|
||||
requests.antMatchers("/public/**").permitAll();
|
||||
requests.anyRequest().authenticated();
|
||||
});
|
||||
http.httpBasic();
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue