SecurityContext is not getting cleared when using SpringBootTest with TestNG - spring

I am working on a SpringBoot application. I am running integration tests on REST APIs using SpringBooTest and TestNG. It is a stateless application. Sessions are not stored.
Context
I have filter that explicitly sets the authentication object into SecurityContext like below
Authentication authentication = new UsernamePasswordAuthenticationToken(contextUser, "", null);
SecurityContextHolder.getContext().setAuthentication(authentication);
// Continue to app.
After this point, I am not using any authentication manager as the token is already validated. I am not explicitly clearing the security context any where.
Problem
When I run a couple of tests from SpringBootTest, the second test still has access to security context set in the first test. When I ran the test in debug mode, I can see SecurityContextHolder.clearContext(); getting called from SecurityContextPersistenceFilter which means context should be cleared.
How do I solve this issue? I would think security context should be always cleared after the request is complete and should not be available for the next test.

I had to set a FilterChainProxy like below for the filters to get executed in Spring Integration test, which would take care of clearing the context.
restLoginMockMvc = MockMvcBuilders.standaloneSetup(loginResource).setControllerAdvice(exceptionTranslator)
.apply(springSecurity(springSecurityFilterChain)).build();

Related

How to properly provide Authentication object (SecurityContext) to application itself?

Most of my application is secured with method level security (AspectJ, but it doesn't matter) and now that I am trying to call some code from within application itself (not controllers, but e.g. EventListener) I can't help to wonder if Spring Security provides some out-of-box way of giving Authentication object to the application itself, otherwise I cannot get past my method security since application has null security objects (Authentication in SecurityContext, if it even exists - depends on situation, You might have to init it first).
Sure I can do something like this (just before running relevant code):
UserDetails ud = User.builder()
.username("APPLICATION")
.password("APPLICATION")
.roles("APPLICATION")
.build();
Authentication auth = new UsernamePasswordAuthenticationToken(ud, ud.getPassword(), ud.getAuthorities());
SecurityContextHolder.getContext()
.setAuthentication(auth);
But is it safe to do this in deployment (security-wise)?
Is there any guarantee on which thread will own this SecurityContext? What about other threads and their tasks?
Once set, can it stay there? Will it for the rest of app's run (can be days/months), context could be reloaded, etc. I lack deep Spring knowledge to know what happens Thread-wise inside Spring.

Updated spring security context not available in session on redirect

Updated spring security context not available on redirect
In one of my controllers - before redirect - I execute the following code:
Credential oldCredential = (Credential) authentication.getCredentials();
Authentication authenticationToken = new AuthenticationToken(new Credential(oldCredential.getCookieValue(), oldCredential.getPassword()),
updatedAccountDetails);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
When I inspect the authentication object after setting it I see the authentication context is correctly updated. After executing the code above I redirect.
After the redirect completes the security context loaded is the old one! That is: when I inspect the security context it contains not the new authentication token.
If I do the following for test - I add the setAttributE(...) after setAuthentication(...) - I get the new authenticationToken in my session but under (obviously) a different key: test. I tried a couple of strategies for the SecurityContextHolder (MODE_THREADLOCAL, MODE_INHERITABLETHREADLOCAL, MODE_GLOBAL) although I think the default MODE_THREADLOCAL is suited for my application.
request.getSession(false).setAttribute("test", authenticationToken);
The strange thing is that if I debug and check in SecurityContextPersistenceFilter the HttpSessionSecurityContextRepository I see that after the redirect the loaded context is actually the new one! For some reason - unknown to me at this moment - this load is however at some later point replaced(?) with the old security context.
Make sure that you've called setAuthenticated(true) otherwise the security context is not updated.

#WithUserDetails and #WithMockUser when using springSecurity() in MockMVC with production authentication logic

After debugging for some time, I found that #WithUserDetails and #WithMockUser dummy the SecurityContext but don't add any fake Authentication Provider, so in case this is used with production code,
SecurityContextHolder is populated with fake SecurityContext containing our dummy Authentication object
Filters take place and all the prod authentication system fail because there is no extra AbstractUserDetailsAuthenticationProvider in ProviderManager, and upon final error, SecurityContext is cleared, so I loose the test mock authentication setting...
So.. is there any built-in way to use test convenient #With.. annotations over production auth code? Is it a bug? Can I change the order of previous bullet points so at least the testing preparation happens later?
Correct, the #WithUserDetails and #WithMockUser are intended when you wish to mock a request that is already authenticated.
So all the filters that do authentication, should not trigger at this point. If you're filters are triggering, then you can rewrite the filters to account for the use case when the authentication already has taken place.
Another option is to rewrite the test to do authentication the way your application expects it.

An Authentication object was not found in the SecurityContext

I have an application exporting web services, with a configured Spring Security SecurityFilterChain (with SecurityContextPersistenceFilter among others, which is required for the rest).
My application also uses Spring Security to secure method invocations.
I have following error when method security is triggered:
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
The 2nd part requires an Authentication in SecurityContextHolder as showed in org.springframework.security.access.intercept.AbstractSecurityInterceptor (line 195):
SecurityContextHolder.getContext().getAuthentication();
But, SecurityContextPersistenceFilter removes it before method invocation is triggered, as shown in
org.springframework.security.web.context.SecurityContextPersistenceFilter (line 84)
SecurityContextHolder.clearContext();
What can I do to have this object in SecurityContextHolder when method invocation is triggered?
Thank you in advance.
I'm using Spring Security 3.0.8-RELEASE
SecurityContextHolder.clearContext() will be called only after request processing completion. So normally all your application logic code will be executed before this line, and there is no problem at all. But the problem may be present if you execute some new thread in your code (by default security context will be not propogated). If this is your case then you can try to force context propogation to child thread. If you use only one thread then make sure that all your code is covered by spring security filter chain (may be you have some custom filter that executed around spring security filter chain?).
OK, my application is placed over Apache CXF DOSGi 1.4 to generate REST endpoints. Apache CXF interceptors cause an unexpected behaviour and SecurityContextHolder.clearContext() is called before finishing the request processing.
More information about this bug can be found here.

How to make a non web thread run with the Spring Security Anonymous Use?

I am using Spring Security 3.1 and I have some code which I execute on web application strartup from the init method of a Servlet. Problems is that there is no valid Authentication object at the time my servlet init method execute. My servlet is configured to run after the Spring has been initialized and spring security is full configured.
How do I make the code in my init method run as the anonymous user in spring security?
How about:
SecurityContextHolder.getContext().setAuthentication(new AnonymousAuthenticationToken(key, login, authorities))
where any non-empty string should do as a key, login like "anonymous", authorities - whichever you want him to have, at least one.

Resources