I'm unable to configure correctly the security in my tests.
My web security configuration:
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**").hasRole("USER")
.and()
.httpBasic()
;
}
}
And my test class:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration
#ContextConfiguration(classes = {Application.class, AppConfig.class, WebMvcConfig.class, WebSecurityConfig.class})
#WebAppConfiguration
public class TestControllerTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
this.mockMvc = webAppContextSetup(wac).dispatchOptions(true).build();
}
#Test
public void getTest() throws Exception {
mockMvc
.perform(get("/api/test"))
.andExpect(status().isForbidden())
;
}
}
I get a 404 status code meaning the security layer is not executed, so it is not configured correctly in my test class.
I tried to switch the classes from #ContextConfiguration to #SpringApplicationConfiguration without success.
Make the following modifications to your code:
#Autowired
private FilterChainProxy filterChainProxy;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
this.mockMvc = webAppContextSetup(wac).dispatchOptions(true).addFilters(filterChainProxy).build();
}
As said in reference for Spring Security 4.0.4:
In order to use Spring Security with Spring MVC Test it is necessary to add the Spring Security FilterChainProxy as a Filter. It is also necessary to add Spring Security’s TestSecurityContextHolderPostProcessor to support Running as a User in Spring MVC Test with Annotations. This can be done using Spring Security’s SecurityMockMvcConfigurers.springSecurity().
Example:
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebAppConfiguration
public class TestControllerTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(wac)
.apply(springSecurity()) //will perform all of the initial setup to integrate Spring Security with Spring MVC Test
.build();
}
Related
Can someone point me to what could be wrong in below code. It is a boot spring 2.6.7 application. When test profile is running, it throws error for all tests like below.
java.lang.IllegalStateException: springSecurityFilterChain cannot be null. Ensure a Bean with the name springSecurityFilterChain implementing Filter is present or inject the Filter to be used
#AutoConfigureMockMvc
#SpringBootTest(classes = some.class)
#ActiveProfiles("test")
public class someTest {
#Autowired
private MockMvc mvc;
#Autowired
private WebApplicationContext webAppContext;
#MockBean
private SomeBean someBean;
#SpyBean
private SomeSpyBean someSpyBean;
#BeforeEach
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(webAppContext)
.apply(springSecurity())
.build();
}
#Test
public void SomeTest1() throws Exception {
String text = "text1";
when(someBean.findStuff(text).thenReturn(Optional.of(new Thingie()));
mvc.perform(multipart("/api/somepath/")
.andExpect(status().isNotFound());
verify(someSpyBean).doStuff();
}
#Test
public void SomeTest2() throws Exception {
String text = "text2";
when(someBean.findStuff(text).thenReturn(Optional.of(new Thingie()));
mvc.perform(multipart("/api/somepath/")
.andExpect(status().isFound());
verify(someSpyBean).doStuff();
}
}
I write a Spring Boot app and I was able to access and test Controller with MockMvc. The issue is that during testing security is not enforced and I can access Controller with no user.
Am I doing anything wrong? Is it intended behavior?
ControllerTest class:
#RunWith(MockitoJUnitRunner.class)
public class ControllerTest {
private MockMvc mockMvc;
#Mock
private Service service;
#InjectMocks
private Controller controller;
private final static String URL = "/test";
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void test() throws Exception {
mockMvc.perform(get(URL))
.andExpect(status().isOk());
}
}
My SecurityConfig StackOverflow QA.
Your examples uses a plain unit test to test your controller. In this setup the Controller is created by Mockito (the controller field is annotated with Mockito's #InjectMocks).
Mockito is not aware of Spring, in consequence no Spring Security will be setup in your test.
You need to use the SpringRunner to run your test. This runner is Spring aware and allows you to properly initialize your controller before the test is run.
The test should look something like this (junit5):
#ExtendWith(SpringExtension.class)
#WebMvcTest(controllers = Controller.class)
public class ControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private Service serviceMock;
#Test
public void test() throws Exception {
mockMvc.perform(get(URL))
.andExpect(status().isOk());
}
}
check our the Spring documentation or some tutorials for further information
https://spring.io/guides/gs/testing-web/
https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html
Today I updated my project from Spring Boot 1.5.9 to 2.1.1, and some of my tests stopped working. When i start the tests, error pops on console:
Field authEntryPoint in com.example.rest.config.SecurityConfig required a bean of type 'com.example.rest.service.auth.entrypoints.AuthenticationEntryPoint' that could not be found.
The problem is I have bean of this type defined in my SecurityConfig class, but I am overriding this configuration in my test package in TestApplication class. Security config is defined there as static inner class. I have tried different approaches including Spring profiles and #Primary annotation, but nothing seems to work and Spring doesn't pick my test configuration like it did before. Only thing that worked was when I deleted the non-test version of SecurityConfig class and test version became only bean of this type.
Can someone tell me how do I override this original configuration or how to turn off Spring Security just for testing? Or maybe there is a way to force Spring not to pick up that non-test #Configuration bean?
SecurityConfig.class
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
AuthenticationEntryPoint authEntryPoint;
#Autowired
BasicAuthenticationProvider basicAuthProvider;
#Autowired
PreAuthenticatedUserDetailsService preAuthUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/rest/query/id/*/user/*",
"/rest/files/**/*").hasAnyRole("CLIENT", "SYSTEM")
.antMatchers("/public/api/management/**/*").hasRole("SYSTEM")
.antMatchers("/public/api/**/*").hasAnyRole("SYSTEM", "USER")
.antMatchers("/rest/**/*").hasRole("SYSTEM")
.and()
.x509()
.userDetailsService(preAuthUserDetailsService)
.and()
.httpBasic()
.authenticationEntryPoint(authEntryPoint)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(basicAuthProvider);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/").antMatchers("/rest/files/name/**");
}
}
Test SpringBootClass with SecurityConfig inside
#SpringBootApplication
public class TestApplication {
#Configuration
#EnableWebSecurity
public static class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").permitAll()
.and().csrf().disable();
}
}
}
Example test from the suite
#RunWith(SpringRunner.class)
#WebMvcTest(DocumentManagementController.class)
public class DocumentManagementControllerTests {
#Autowired
MockMvc mvc;
#MockBean
SystemMetadataService systemMetadataService;
#MockBean
CustomMetadataService customMetadataService;
#MockBean
PrinterService printerService;
#MockBean
EventLoggerService eventLoggerService;
#Captor ArgumentCaptor<String> systemCaptor;
#Captor ArgumentCaptor<String> clientCaptor;
#Captor ArgumentCaptor<Boolean> holdCaptor;
#Captor ArgumentCaptor<String> retentionCaptor;
#Captor ArgumentCaptor<String> objectPathCaptor;
#Captor ArgumentCaptor<Boolean> accessCaptor;
#Captor ArgumentCaptor<Boolean> manualProcessingCaptor;
#Captor ArgumentCaptor<Boolean> incorrectCaptor;
#Captor ArgumentCaptor<Integer> statusCaptor;
#Captor ArgumentCaptor<Boolean> noTemplateCaptor;
#Test
public void setDocumentAccess_givenProperData_shouldReturnOk() throws Exception {
when(customMetadataService.setDocumentAccess(anyString(), anyBoolean()))
.then(inv -> new HcpCreateObjectResult(inv.getArgument(0)));
Boolean accessForbidden = true; String objectPath = "path";
mvc.perform(get("/rest/management/access/forbid/"+accessForbidden+"?objectPath="+objectPath))
.andExpect(status().isOk());
verify(customMetadataService).setDocumentAccess(objectPathCaptor.capture(), accessCaptor.capture());
assertThat(objectPathCaptor.getValue(), is(equalTo(objectPath)));
assertThat(accessCaptor.getValue(), is(equalTo(accessForbidden)));
}
I managed to do make this work using #Profile and #ActiveProfiles. But i had to extract my static inner #Configuration class to another java file and then it automagically started to work. Still haven't found why it worked in earlier version of Spring Boot
I confused with configuration for unit test:
This's my test class:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
public class MyTest {
private MockMvc mockMvc;
#Autowired
private MyController myController;
#Before
public void setUp() {
mockMvc = MockMvcBuilders
.standaloneSetup(myController)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
}
#Test
public void test() {
}
#Configuration
static class Config {
#Bean
MyController myController() {
return new MyController();
}
}
}
When I run it, I get:
java.lang.IllegalStateException: springSecurityFilterChain cannot be
null. Ensure a Bean with the name springSecurityFilterChain
implementing Filter is present or inject the Filter to be used.
How to configure it properly?
I have created the Home Controller below. This controller fetches the 5 dummy posts I have created in the "PostRepository" class through PostService class.
#Controller
public class HomeController {
#Autowired
PostService postService;
#RequestMapping("/")
public String getHome(Model model){
model.addAttribute("Post", postService);
return "home";
}
}
I have implemented the following test..
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {WebConfig.class})
#WebAppConfiguration
public class ControllerTest {
#Test //Test the Home Controller
public void TestHomePage() throws Exception{
HomeController homeController = new HomeController();
MockMvc mockMvc = standaloneSetup(homeController).build();
mockMvc.perform(get("/"))
.andExpect(view().name("home"))
.andExpect(model().attributeDoesNotExist("Post"));
}
}
The test has successfully passed. But the attribute should exist.
You are mixing two incompatible features of Spring's testing support.
If you instantiate the controller within the test, you need to use MockMvcBuilders.standaloneSetup().
If you are using the Spring TestContext Framework (i.e., #ContextConfiguration, etc.), then you need to use MockMvcBuilders.webAppContextSetup().
Thus, the following is the appropriate configuration for your test.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = WebConfig.class)
#WebAppConfiguration
public class ControllerTest {
#Autowired
WebApplicationContext wac;
#Autowired
PostService postService;
#Test
public void TestHomePage2() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
mockMvc.perform(get("/"))
.andExpect(view().name("home"))
.andExpect(model().attribute("Post",postService));
}
}
Regards,
Sam (author of the Spring TestContext Framework)
If that's the complete code, then you are missing
#RunWith(SpringJUnit4ClassRunner.class)