spring-security: SecurityMockMvcConfigurer regression in Spring Boot 2.2.1
Summary
SecurityMockMvcConfigurer$DelegateFilter is not null-safe, if no springSecurityFilterChain is available.
Actual Behavior
java.lang.NullPointerException
at org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurer$DelegateFilter.doFilter(SecurityMockMvcConfigurer.java:132)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:183)
at io.restassured.module.mockmvc.internal.MockMvcRequestSenderImpl.performRequest(MockMvcRequestSenderImpl.java:219)
at io.restassured.module.mockmvc.internal.MockMvcRequestSenderImpl.sendRequest(MockMvcRequestSenderImpl.java:448)
at io.restassured.module.mockmvc.internal.MockMvcRequestSenderImpl.put(MockMvcRequestSenderImpl.java:505)
at io.restassured.module.mockmvc.internal.MockMvcRequestSenderImpl.put(MockMvcRequestSenderImpl.java:101)
at com.example.demo.rest.DemoResourceTest.testEcho(DemoResourceTest.java:36)
Expected Behavior
No exception should be thrown even when no springSecurityFilterChain is available.
Configuration
Our application uses spring security, and org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers is also in the test classpath. In a REST endpoint test (Mockito+RestAssured based, not full integration test, and security is not of interest), springSecurityFilterChain is not available.
Version
The issue exists in spring-security-test 2.2.x. In spring-security-test 2.0.x, it works fine.
Related to issue https://github.com/spring-projects/spring-security/issues/7688
Sample
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>security-mock-mvc-config-regression</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Sample for SecurityMockMvcConfigurer regression</name>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>spring-mock-mvc</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.example.demo.rest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoResource {
@PutMapping(value = "/echo")
public ResponseEntity<String> echo(@RequestBody String message) {
return ResponseEntity.ok(message.toLowerCase());
}
}
package com.example.demo.rest;
import static io.restassured.module.mockmvc.RestAssuredMockMvc.given;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.MockitoAnnotations;
import org.springframework.http.HttpStatus;
import io.restassured.module.mockmvc.RestAssuredMockMvc;
public class DemoResourceTest {
@InjectMocks
private DemoResource target;
@BeforeEach
public void beforeEach() {
MockitoAnnotations.initMocks(this);
RestAssuredMockMvc.standaloneSetup(target);
}
@Test
public void testEcho() {
String input = "TEST";
String message = given()
.body(input)
.when()
.put("/echo")
.then()
.statusCode(HttpStatus.OK.value())
.extract()
.response().asString();
assertThat(message).isEqualTo(input.toLowerCase());
}
}
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 16 (10 by maintainers)
Commits related to this issue
- SecurityMockMvcConfigurer$DelegateFilter is not null-safe This commit adds null-check to getter method, so instead of NPE an IllegalStateException will be thrown with additional details. Fixes gh-77... — committed to dadikovi/spring-security by dadikovi 4 years ago
- SecurityMockMvcConfigurer$DelegateFilter is not null-safe This commit adds null-check to getter method, so instead of NPE an IllegalStateException will be thrown with additional details. Fixes gh-77... — committed to spring-projects/spring-security by dadikovi 4 years ago
Have you already tried the RestAssured support for turning off security?
For example. you could do something like:
And then in your test doing:
The RestAssured folks may have a different opinion on the preferred way to shut off security for a request.
I prefer the above solution since Spring Security’s test support probably shouldn’t be added at all if Spring Security isn’t configured. The
SecurityMockMvcConfigurer#beforeMockMvcCreatedimplementation is written with this in mind.However, you could also consider doing:
Which would replace the spring security filter chain for all tests in the class.
Would one of these address your concern?
Thanks @jzheaux - I figured out eventually the way to set up a global config which worked for me in the CDC case:
can I pick this up?