Expected Behavior

After calling MockMvc, I want to be able to make assertions that depend on the SecurityContext defined by the test.

Current Behavior

Currently, after performing any MockMvc request, the SecurityContext is cleared by ThreadLocalSecurityContextHolderStrategy#clearContext() which causes the following exception to be thrown when you use SecurityContext dependent code during assertions:

java.lang.IllegalArgumentException: Authentication object cannot be null

Details can be found here in SO: https://stackoverflow.com/questions/51622300/mockmvc-seems-to-be-clear-securitycontext-after-performing-request-java-lang-il/

Context

  • Goal: I want to keep the SecurityContext during the whole test execution.
  • Workaround: I could disable filters through @AutoConfigureMockMvc(addFilters = false). Does not seem like a nice option in all cases.
  • Current solution in my project using a MockMvcBuilderCustomizer
public class MockMvcTestSecurityContextPropagationCustomizer implements MockMvcBuilderCustomizer {

    @Override
    public void customize(ConfigurableMockMvcBuilder<?> builder) {
        builder.alwaysDo(result -> {
            log.debug("resetting SecurityContextHolder to TestSecurityContextHolder");
            SecurityContextHolder.setContext(TestSecurityContextHolder.getContext());
        });
    }

}

Proposal My proposal is that this propagation could be done by the framework itself in org.springframework.boot.test.autoconfigure.web.servlet.MockMvcSecurityConfiguration.SecurityMockMvcBuilderCustomizer (or a another separate configuration within MockMvcSecurityConfiguration) but I am not sure if I miss some negative impact by doing that in general.

Comment From: rwinch

Thanks for the report. I don't think we can do this automatically as it would be very surprising around flows like log out.

What I could see is Spring Security creating org.springframework.security.test.web.servlet.response.SecurityMockMvcResultHanlders.copyTestSecurityContextHolderToSecurityContextHolder() which performs this logic in the returned ResultHandler. I'd have to give some thought to the name of this method as it is very verbose and I'd like something a bit more concise.

Then users would be in charge of applying the ResultHandler to MockMvc.

Would you be interested in submitting a pull request?

Comment From: marcusdacoregio

What do you think about withSecurityContext(Consumer<SecurityContext>) for the method signature? I think that this way the reading of the code would be better:

this.mockMvc.perform
    ...
    .andDo(withSecurityContext((context) -> SecurityContextHolder.setContext(context)));

And with the Consumer, users can specify any destination for the SecurityContext.

Comment From: sniffertine

The problem here is for me, that you need to understand the issue and how to solve it. I like the suggestion of @rwinch more.

What about: SecurityMockMvcResultHanlders.keepTestSecurityContext() and explain the details in Javadoc?

Comment From: marcusdacoregio

Different test scenarios that use the SecurityContext may appear in the future, so I'm more inclined in leaving the user do what they want with the SecurityContext and explain such scenarios in the documentation, alongside other Spring MVC Test Integration, by adding a new SecurityMockMvcResultHandlers section.

Let's see what @rwinch thinks fits better.

Comment From: marcusdacoregio

I had a chat with @rwinch and we think it's better to keep it more specific. And @jzheaux suggested that we could use exportSecurityContext for the method name. I'll go ahead and change the PR based on these suggestions.