spring boot 2 test ignores ResourceServerConfigurerAdapter - spring

I have a oauth2 resource server with the following rules:
http.authorizeRequests()
.antMatchers("/api/client/all").hasRole("ADMIN")
.antMatchers("/api/client/create", "/api/client/update", "/api/client/delete/*").hasAnyRole("USER", "ADMIN")
.antMatchers("/api/client/*").hasAnyRole("USER", "ADMIN")
;
I can verify that the urls are secured by using postman and sending bearer tokens from different users causes authorization access for users you don't have he right roles. So my code works hen running the application. Now I want to write tests for my resource server. I have noticed something weird going on. No matter what I do spring only using the AffirmativeBased decision manager during testing. I can't get spring to test if users have a certain role. This does happen when running the application however during junit testing spring securty isn't matching the roles. My test class has the following annotation:
#WebMvcTest(ClientController.class)
which should set spring security. I also inject the mockmvc like this:
#Autowired
private MockMvc mockMvc;
I have seen people building their own mockmvc instead of injecting it. For example like this:
MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
Which is the right way? And why wont spring security match the roles during testing?

Related

How to mock JwtDecoder in Spring Boot for integration testing of authenticated controllers? [duplicate]

This question already has answers here:
How do I test main app without spring security?
(2 answers)
Closed 4 days ago.
In my Spring Boot application, there are some authenticated controllers.
The operation mode is "OAuth2 resource server", so my application relies on some arbitrary OAuth2 authorization server. (Let's say it's Keycloak, though it should not affect the way of mocking)
So, the question is:
What is the right way to mock JwtDecoder, in order to be able to pass some static strings as the bearer tokens?
(Please remember, it's a third party server responsible for the token issuing; So I cannot rely on it in tests. I want to mock it away to be able to run tests offline for example)
An example of what I expect to happen:
I mock JwtDecoder (let's pretend I've created some map of <token string, UserData>)
I make a MockMvc-based http call to the authenticated controller with this static string in the Authorization header (Authorization: Bearer STATIC_STRING). The controller test is decorated with #SpringBootTest and #AutoConfigureMockMvc.
I expect to have JwtAuthenticationToken filled with data from UserData from the map of the mocked JwtDecoder.
I've already tried to just create a bean of JwtDecoder implementation with all the described features. I see this bean is added to the configuration, but still whole test ends up in AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext. So I presume that JwtDecoder is never called (checked that with the debugger), and the whole testing setup is misconfigured, but I don't know for sure what to change.
What do I miss?
With MockMvc (as well as WebTestClient in reactive apps) the Authorization header is just ignored (not decoded, validated, introspected or whatever).
The test security context is to be set directly (SecurityContextHolder.createEmptyContext().setAuthentication(auth);) or with the help of either:
test annotations (like #WithMockUser, but this one is not quite adapted to mocking OAuth2 identities). Refer to those that I created in this lib for OAuth2
Request post-processors for MockMvc
mutators for WebTestClients
More details in this other answers:
How to write unit test for SecurityConfig for spring security
How do I test main app without spring security?

Is it possible to test specific Spring REST endpoint security and avoid bootstrapping database connection?

We have a couple of Spring tests that call a secured controller endpoints. Our goal is to assure that absence of particular user roles will result into HTTP 403 status.
Our issue is that execution of those tests also bootstraps DB connection which we don't actually need.
I've already tried countless number of all kind of annotations and manual configurations to avoid initialization of DB connection but so far without luck. Can you please share example how to do that?
We use Spring Boot 2.7.
Yes, you can use #WebMvcTest, take a look at the docs. In summary, using #WebMvcTest will only bootstrap the Spring MVC components and avoid loading other application's layers. This annotation also automatically configures Spring Security for you, therefore you can test authentication/authorization rules.
Example:
#WebMvcTest(UserVehicleController.class)
class MyControllerTests {
#Autowired
private MockMvc mvc;
#MockBean
private UserVehicleService userVehicleService;
#Test
#WithMockUser(roles = "ADMIN")
void testAdminSuccess() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andExpect(content().string("Honda Civic"));
}
#Test
#WithMockUser(roles = "USER")
void testUserForbidden() throws Exception {
this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isForbidden());
}
}

Spring security client detail is going as user detail in DaoAuthenticationProvider

Spring Boot, Security OAuth2 implementation, default token endpoint (/oauht/token) is working fine. However, when I send a request to new endpoint at /oauth/http/token it is throws Bad Credentials because of the following reason:
FilterChainProxy triggers around 12 filters and out of which one is BasicAuthenticationFilter. It uses UserDetailsService of DaoAuthenticationProvider class to fetch user data. For client authentication this should be ClientDetailsService but, for some reason this is always UserDetailsService and because of this client credentials goes to UserRepository and fails. This class does initialize properly because default /oauth/token works fine.
I tried to inject existing Authentication Manager in BasicAuthenticationFilter and added that as a filter in ResourceServerConfigurerAdapter but that didn’t make any difference. It does change Authentication Manager provider from AnonymousAuthenticationProvider to DaoAuthenticationProvider but UserDetailsService still remains UserDetails.
Request at /oauth/http/token, this doesn't work. Code is almost same as postAccessToken() of org.springframework.security.oauth2.provider.endpoint.TokenEndpoint
In above screenshot we can see that userDetailsService is UserDetailsServiceImpl and due to this client details present in request header as Basic dGVzdDpwYXNzd29yZA== is going to user repository and checking at user table instead of going to client repository and checking at client table.
Request at /oauth/token, this works
FilterChainProxy maintains not a single filter chain but a list of SecurityFilterChain-s.Each security filter chain contains a request matcher and a list of filters. So you will have several instances of BasicAuthenticationFilter in these different chains.
Which filter chain will be triggered depends on the incoming request and the decisions of the request matchers.
/oauth/token triggers the chain which is created by spring oauth and uses ClientDetailsService at the end.
/oauth/http/token triggers another chain created by your web security configuration and uses user details service.
So... that is the reason. To see how the chains are created on startup you may enable the security debug, e.g. in application.yml
logging:
level:
org.springframework.security: DEBUG
Then you will see the oauth security chain creation:
Creating filter chain: OrRequestMatcher [requestMatchers=[Ant [pattern='/oauth/token'], Ant [pattern='/oauth/token_key'], Ant [pattern='/oauth/check_token']]], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#4ce4e309, org.springframework.security.web.context.SecurityContextPersistenceFilter#16331720, org.springframework.security.web.header.HeaderWriterFilter#60ef29a5, org.springframework.security.web.authentication.logout.LogoutFilter#4c9638cc, org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter#9eefda5, org.springframework.security.web.authentication.www.BasicAuthenticationFilter#16d090e9, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#484a9950, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#1c4fefe8, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#12082780, org.springframework.security.web.session.SessionManagementFilter#20a49b7b, org.springframework.security.web.access.ExceptionTranslationFilter#24313d10, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#47ce08d2]
Note the request matchers.
UPDATE: If you want to 'remap' the endpoint to your own endpoint you may reconfigure that.
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.pathMapping("/oauth/token", "/oauth/http/token");
}

Spring Oauth for Web Application

I have a query I am struggling to come to a decent answer with and hoping by vocalizing it someone might be able to guide me or advise me in the right direction.
All our current web applications are build using Spring, which includes the security which is handled by Spring Security module of course.
We are exploring the opportunities of integrating some of our new android projects into these web applications.
After some research and guidance, all flags point towards OAuth2 implementation in Android App and using that to obtain access to relevant parts of the web application server via Restfull calls.
What I am trying to understand now is can we and should we replace our existing Spring Security implementation in our web application and replace it with Spring Oauth2 equivalent.
The overall goal would to be able to have a single security solution that we would use for both website login, app login, and any API implementations that would be exposed to other web applications.
If anyone can also provide a link to a Spring Oauth2 Java Config (not-XML) setup where a user logs in and accesses a page based on their role in a unrestful manner, would also be extremely helpful. Most examples we have found were either extremely complex, focused solely on restfull calls, or only XML configurations.
Seems you did not hear about spring-cloud-security project which extremely simplifies working with oauth2 providers. Take a look at:
http://cloud.spring.io/spring-cloud-security/
There are samples available on github ( right side of above page ). It shows how to set up in case you want to use just one oauth2 provider - check project which shows how to do that for github https://github.com/spring-cloud-samples/sso . You do most of this stuff through configuration in application.yml file + add #EnableOAuth2Sso annotation so all machinery starts.
For 'normal' interaction with your web app its pretty straighforward. If you need your app to work as an api for others then you have to maintain tokens, add them to request etc. If client of your api is also your code which you can change then you may use OAuth2RestTemplate class which take care about tokens, so you do queries to your api as it was usual/not secured endpoint.
If you need to use for example two different providers for different paths then its more complicated, you have to configure them all like:
#Bean
public OAuth2RestTemplate facebookOAuth2RestTemplate(OAuth2ClientContext clientContext) {
return new OAuth2RestTemplate(facebookOAuth2ResourceDetails(), clientContext);
}
#Bean
public OAuth2ProtectedResourceDetails facebookOAuth2ResourceDetails() {
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
resource.setAccessTokenUri(tokenUri);
resource.setId("id");
resource.setUserAuthorizationUri(authorizationUri);
resource.setUseCurrentUri(false);
resource.setPreEstablishedRedirectUri(redirectUri);
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
resource.setTokenName("tokenname");
resource.setAuthenticationScheme(AuthenticationScheme.query);
resource.setClientAuthenticationScheme(AuthenticationScheme.form);
return resource;
}
and decide which instance of OAuth2RestTemplate to use in which case.
//update
If you want to exchange spring security core with authorizing users by some oauth2 provider you can extends OAuth2SsoConfigurerAdapter class:
#Configuration
#EnableOAuth2Sso
public class WebSecurityConfig extends OAuth2SsoConfigurerAdapter {
#Override
public void match(RequestMatchers matchers) {
matchers.antMatchers("/admin");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN")
.and()
.logout().logoutUrl("/logout").permitAll()
.logoutSuccessUrl("/");
}
So /admin will be now protected, and user redirected to any authorization server you specify. It requires oauth configuration in application.yml.
spring:
oauth2:
client: # Sauron
clientId: app_clientid
clientSecret: app_secret
accessTokenUri: http://authorizationserver/oauth/token
userAuthorizationUri: http://authorizationserver/oauth/authorize
clientAuthenticationScheme: header
resource:
tokenInfoUri: http://authorizationserver/oauth/check_token
preferTokenInfo: false
Thats why I wrote before that its easy to use just one auhorization server, but in case you need more then its more complicated.

How to test REST in spring app with spring security

I've got spring web application with jersey rest services. However rest is secured via spring security and login process is very hard to perform from unit test code. I'd like to test rest services with whole spring security disabled. Is it even possible?
One of the advantages of annotation based web services is that you can unit-test them easily.
class WebServiceEndpoint {
#Path("/foo/{fooId}")
#POST
#Produces({ MediaType.APPLICATION_XML })
public Response doFoo(#PathParam("fooId") Integer fooId) {
/// ... web service endpoint implementation
}
}
If you're using Spring's servlet filter for security, then there shouldn't be any security-related code in the doFoo method, so you can just create a new WebServiceEndpoint class and call the method. So that's one way of 'disabling' security.
When you say the login process is 'hard', what do you mean? If you've succeeded in logging in once, then you can just reuse the same code in your other unit tests (e.g. in a #Before method).
Just test it as a pojo. Pass in whatever, return whatever, don't load an app context at all - that would be an integration test.
The ability to easily test functionality without the framework loaded is one of the key advantages of spring.
You don't say what's "hard," so I'm assuming that you've got something in your REST service, i.e. in the java method that you want to test, which requires authentication results. Spring has utilities for mocking the authentication results. For example, you can do the following in a #Before setup method:
Object principal = null; // fix this
Object credentials = null; // fix this
Authentication auth = new org.springframework.security.authentication.TestingAuthenticationToken(principal, credentials);
SecurityContextHolder.getContext().setAuthentication(auth);
But again, you haven't said what problem you're actually trying to solve...

Resources