How to test REST in spring app with spring security - spring

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...

Related

How Do I Access The Spring Boot Startup Actuator During A Test

I would like to record the startup information about my application during a Spring Boot test. I have the startup actuator configured and working in Spring Boot 'bootrun' mode. However, when I try to access that actuator during a test using a TestRestTemplate, I get a 404 error.
I have written an example program that demonstrates the problem. The issue isn't with acutators overall as I have the metrics and health actuators working in the same test. Just the startup actuator.
The example code is on GitHub
I have a solution for this so I thought I would post it. For complete details, see the original repo in GitHub and check the solution branch.
One possible way to enable ApplicationStartup data collection during a Spring Boot Test is to create a ContextCustomizer. This allows you to get into the testing context early enough to record all of the data that you are looking for. The ContextCustomizer should have a single static BufferingApplicationStartup that it registers as a singleton bean into the test context's bean factory. It also needs to set the bean factory's ApplicationStartup because that will be passed to the SpringApplication just before it is run.
Here is the snippet of the customizer that holds the key:
#Override
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Object possibleSingleton = beanFactory.getSingleton(BEAN_NAME);
// The only way it wouldn't be an instance of a BufferingApplicationStartup is if it is null or we haven't
// run yet (and it is the DefaultApplicationStartup). In either case, jam our BufferingApplicationStartup
// in here.
if(!(possibleSingleton instanceof BufferingApplicationStartup)) {
beanFactory.registerSingleton(BEAN_NAME, APPLICATION_STARTUP);
beanFactory.setApplicationStartup(APPLICATION_STARTUP);
}
}
When you do this, make sure you implement a good equals and hashCode for your customizer or else you will break the test context caching and you will refresh your test context with every test class. Since the only relevant part of the customizer is the static BufferingApplicationStartup, I chose to return its hashcode.
Finally, don't forget to add your ContextCustomizerFactory to the src/test/resources/META-INF/spring.factories or else the rest of the Spring Boot testing support won't see your customizer.
Once this is all setup, you can access the Startup Actuator endpoint just like you would any other actuator.

How can I test a secured endpoint with Awaitility in Spring boot?

I'm using spring boot and I want to assert an asynchronous side effect by calling a secured endpoint with MockMvc.
I have been using Awaitility, but apparently the mocked security context is lost when executing in a different thread.
I couldn't find a way of passing the context, I tried with SecurityContextHolder.setContext() but it didn't work, I guess spring's MockMvc stores the context in a different way.
#Test
#WithMockUser(authorities = "admin", username = "user")
void shouldRunSideEffectAsync() throws Exception {
mockMvc.perform(post("/foo")).andExpect(status().isAccepted());
await()
.atMost(TIMEOUT)
.untilAsserted(() -> mockMvc.perform(get("/foo")).andExpect(status().isOk()));
}
The GET would return 404 for a while and then 200 when the async task is completed. However this will always return 403 as the MockUser info is lost.
How can I solve this?
You almost got it. Security for MockMvc is implemented by TestSecurityContextHolderPostProcessor, which uses the TestSecurityContextHolder to set/get the security context. That is just a wrapper around the SecurityContextHolder.
So you can use TestSecurityContextHolder.setContext() in the awaitility thread and it should work.

How to inject Feign Client with out using Spring Boot and call a REST Endpoint

I have two Java processes - which get spawned from the same Jar using different run configurations
Process A - Client UI component , Developed Using Spring bean xml based approach. No Spring Boot is there.
Process B - A new Springboot Based component , hosts REST End points.
Now from Process A , on various button click how can I call the REST end points on Process B using Feign Client.
Note - Since Process A is Spring XML based , right at the moment we can not convert that to Spring boot. Hence #EnableFeignClients can not be used to initialise the Feign Clients
So Two questions
1) If the above is possible how to do it ?
2) Till Process A is moved to Spring boot - is Feign still an easier option than spring REST template ?
Feign is a Java to HTTP client binder inspired by Retrofit, JAXRS-2.0, and WebSockets and you can easily use feign without spring boot. And Yes, feign still better option to use because Feign Simplify the HTTP API Clients using declarative way as Spring REST does.
1) Define http methods and endpoints in interface.
#Headers({"Content-Type: application/json"})
public interface NotificationClient {
#RequestLine("POST")
String notify(URI uri, #HeaderMap Map<String, Object> headers, NotificationBody body);
}
2) Create Feign client using Feign.builder() method.
Feign.builder()
.encoder(new JacksonEncoder())
.decoder(customDecoder())
.target(Target.EmptyTarget.create(NotificationClient.class));
There are various decoders available in feign to simplify your tasks.
You are able to just initialise Feign in any code (without spring) just like in the readme example:
public static void main(String... args) {
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
...
}
Please take a look at the getting started guide: feign on github

Prevent direct call to my spring boot rest API

I have several REST services (on spring boot) used by a javascript file (website) and these services can be called directly by anyone (and you can see the API url/parameters with the developper console on any browser).
So I would like to prevent the direct calls to the API, exept of course from the front side of my app.
I saw that I can use an API key with spring security but is it reliable? Since I think you can see the key if you intercept the message with developper console.
What you can do are the following :
Disable CORS in your springboot application by setting the following globally or per endpoint as you wish.
To set the CORS per endpoint :
#CrossOrigin(origins = "http://localhost:9000")
#GetMapping("/greeting")
public Test testing(#RequestParam(required=false, defaultValue="Test") String name) {
System.out.println("in test");
return new Testing(10, String.format(template, name));
}
You can use spring security to preauthorize your controller endpoints to make sure that only the authorized has access to the controller.
Like for example :
#RestController
#RequestMapping({"/v2/"})
public class ExampleTestController {
#PreAuthorize("hasAuthority('ROLE_ADMIN')")
#RequestMapping(value = "/test", method = RequestMethod.GET)
String test() {
return "Hello";
}
}
Using spring security is safe, as the user is always validated before access is granted . Even while using Oath2 the key generated is after validating the user login and the key can be used to validate every request to the controller by passing it in the header or using it in the rest template.
Another way of isolating your rest endpoints is by using the load balancer (or ngnix or anything) to block requests to these endpoints from outside your domain.

How to mock REST service response in actual code flow for lower environments

I have a service which calls the REST Api and that REST API call is chargeable, so i want when we deploy the code in Dev & QA environment, mock response should be returned while actual functionality testing.
How can i achieve that ?
Mock Rest API response in actual code flow
The endpoint for your api shouldn't be hard-coded in the code, instead you can set the endpoint in a properties file.
ie. In a file like .../myapp/src/resources/application.properties
some.service.api.endpoint=someservice.com/api
Then in your code you can use Spring's #Value annotation to get the value.
#Service
public class SomeServiceApi {
#Value("some.service.api.endpoint")
private String endpoint;
// ...
While in development, the property can point to some mock endpoint that you've set up yourself, or if the api supports it, a sandbox endpoint for the api.

Resources