NPE ocurred when used `WebTestClient.mutateWith() ` in end to end tests - spring-security

When I wrote some tests for webflux applications. And I tried to add credentials via mutateWith(mockUser().password("password")) in WebTestClient, but it caused NPE thrown.
I used bindToServer to connect test client to the running remote APIs, and tried to use mutateWith(mockUser().password("password")) add basic authentication to the request. It throws a NPE when the tests.
Updated Source codes: https://github.com/hantsy/spring-reactive-sample/blob/master/security-method/src/test/java/com/example/demo/IntegrationTests.java#L118-L127

After I explored the source codes of mockUser, it requires a MockServer environment to run the tests, but it was run as end 2 end in my sample.
Changed mutateWith(mockUser().password("password")) to .mutate().filter(basicAuthentication("user", "password")).build(), the NPE was disappeared.
Hope this helpful for you.
#Test
public void deletingPostsWhenUserCredentialsThenForbidden_mutateWith()
throws Exception {
this.rest
.mutate().filter(basicAuthentication("user", "password")).build()
.delete()
.uri("/posts/1")
.exchange()
.expectStatus().is4xxClientError()
.expectBody().isEmpty();
}
Updated Source codes: https://github.com/hantsy/spring-reactive-sample/blob/master/security-method/src/test/java/com/example/demo/IntegrationTests.java#L118-L127

Related

How to enable GlobalMethodsSecurity when unit test with MockMvc of standaloneSetup

SpringSecurity's #PreAuthorize and #PostAuthorize is ignored when unit testing with MockMvc. But it's OK when access by browser of Postman while normally started the application
I am using Spring 4.3 and Spring security 4.2, not the spring boot. I am using MockMvcBuilders.standaloneSetup to test the controller only. and don't want to use webAppContextSetup to involve the entire application to test.
After check the spring security's source code, I found that the Pre and PostAuthorize is checking by org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice and org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice. But the controller is not include by org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource.
I think this is caused by the controller is not initialized by Spring, so I try to register it to the BeanFactory, but it also fail.
Testing code:
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = standaloneSetup(controllers)
.setValidator(validator)
.apply(springSecurity(filterChainProxy))
.alwaysDo(print())
.build();
}
public void itWillFailWhenUpdateOtherOrg() {
CurrentUser user = new CurrentUser();
user.setOrgId(1);
user.setUsername("testuser");
mockMvc.perform(put("/orgs/-1")
.contentType(APPLICATION_JSON)
.content("{\"name\":\"RootOrg\",\"parent\":100}")
.with(user(user))).andExpect(status().isForbidden());
verify(orgService, never()).update(any());
}
Controller code:
#PutMapping("/org/{id}")
#PreAuthorize("principal.orgId == #orgDO.parent")
public OrgDO update(#PathVariable Integer id, #RequestBody OrgDO orgDO) {
}
When testing, the status code is 200, but not 403.
java.lang.AssertionError: Status
Expected :403
Actual :200
I expect the put request will fail and return status code 403, because of the principal.orgId != #orgDO.parent.
Be sure to NOT include all class to the Spring context, I just want to test the controller class.
Thank you very much.
After few hours of digging here is why:
MockMvcBuilders.standaloneSetup normally get passed a controller instantiated manually (not with Spring and therefore not with AOP). Therefore the PreAuthorize is not intercepted and security check is skipped. You can therefore either #Autowire your controller and pass it to MockMvcBuilders.standaloneSetup (which maybe kind of defies the purpose of using standalone setup since it's alos create the rest controller...) or simply use a WebApplicationContext: MockMvcBuilders.webAppContextSetup with an autowired WepAppContext.

#Inject into Jax-rs resource: CWWAM0002E: An exception occurred while merging an annotation into deployment descriptor:

I am getting a weird exception when I deploy Rest sevice locally to my WAS 85 environment in eclipse:
CWWAM0002E: An exception occurred while merging an annotation into deployment descriptor: com.ibm.wsspi.amm.merge.MergeException: Unable to find EnterpriseBean for class WackyDoodleResource
com.ibm.wsspi.amm.merge.MergeException: Unable to find EnterpriseBean for class WackyDoodleResource
This is not my real code but here is how I set it up with names changed to protect the innocent:
#Path("/wacky-doodle-resource")
public class WackyDoodleResource{
#Context UriInfo uriInfo
#Context SecurityContext securityContext;
#Inject WackyDoodleEJB wackyDoodleEJB;
#POST
#Consumes(MediaType.APPLICATIONI_JSON)
#RolesAllowed("WACKYDOODLES")
#Produces(MediaType.APPLICATIONI_JSON)
public Response createWackyDoodle(WackyDoodleRequestRO requestRO{
String response = null;
response = wackyDoodleEJB.createWackyDoodle(requestRO)
}
return Response.ok(response)).build();
}
#Default
#Singleton
public class WackyDoodleEJB implements IWackyDoodleEJB{
public String createWackyDoodle(WackyDoodleRequestRO req){
System.out.println("Do Something Wacky!");
}
}
public interface IWackyDoodleEJB{
public String createWackyDoodle(WackyDoodleRequestRO request);
}
(Simple recreation of my more complex code for illustration purposes)
I see that exception when I deploy my ear to my local Websphere server. The application appears to start up and deploy just fine (if you don't pay attention to what you find in the logs). However, when I attempt to hit any of my http request #myResources, I get this oddly nondescript message
E com.ibm.ws.webcontainer.internal.WebContainer handleRequest SRVE0255E: A WebGroup/Virtual Host to handle / has not been defined.
I suspect it has something to do with my ear not publishing correctly (the exception in my question). I honestly do not know however. So, what could be happening here? This seems like it should be pretty boilertplate stuff?
The error suggests that I should Turn my regular resource pojo into an EJB? If I add, for example, the stateless #nnotation to my class, the above exception in the logs goes away, but I am still not able to hit my sevice resources. I get the same nondescript exception. I am at a loss and I have been looking at this for an hour. If you could point me in any direction I would appreciate it.

Request is blocked in end 2 end testing in a webflux application

Another problem I encountered in end 2 end tests, setting invalid credentials will cause the sent request blocked till it is timeout.
Source codes: https://github.com/hantsy/spring-reactive-sample/blob/master/security-method/src/test/java/com/example/demo/IntegrationTests.java#L83-L94
#Test
public void savingPostsWhenInvalidCredentialsThenUnauthorized() throws Exception {
this.rest
.post()
.uri("/posts")
.attributes(invalidCredentials())
.body(BodyInserters.fromObject(Post.builder().title("title test").content("content test").build()))
.exchange()
.expectStatus().isUnauthorized()
.expectBody().isEmpty();
}
Another case is if the user does not has permission, also caused the request blocked.
#Test
public void deletingPostsWhenUserCredentialsThenForbidden_mutateWith() throws Exception {
this.rest
.mutate().filter(basicAuthentication("user", "password")).build()
.delete()
.uri("/posts/1")
.exchange()
.expectStatus().is4xxClientError()
.expectBody().isEmpty();
}
I am using Reactor Netty as runtime in this sample, the similar tests are passed in the ApplicationTests(used bindToApplicationContext in a mock environment), and failed in IntegrationTests(which used bindToServer in end 2 end testing).

Error in Zuul SendErrorFilter during forward

When my Zuul Filter is unable to route to a configured URL, the 'RibbonRoutingFilter' class throws a ZuulException saying "Forwarding error" and the control goes to the 'SendErrorFilter' class.
Now when the SendErrorFilter class tries to do a forward, another exception happens during this forward call.
dispatcher.forward(ctx.getRequest(), ctx.getResponse());
The exception happening during this forward call is
Caused by: java.lang.IllegalArgumentException: UT010023: Request org.springframework.cloud.netflix.zuul.filters.pre.Servlet30WrapperFilter$Servlet30RequestWrapper#6dc974ea was not original or a wrapper
at io.undertow.servlet.spec.RequestDispatcherImpl.forward(RequestDispatcherImpl.java:103) ~[undertow-servlet-1.1.3.Final.jar:1.1.3.Final]
at org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilter.run(SendErrorFilter.java:74) ~[spring-cloud-netflix-core-1.0.0.RELEASE.jar:1.0.0.RELEASE]
at com.netflix.zuul.ZuulFilter.runFilter(ZuulFilter.java:112) ~[zuul-core-1.0.28.jar:na]
at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:197) ~[zuul-core-1.0.28.jar:na]
Finally when the control comes to my custom ZuulErrorFilter , i do not get the original exception. Instead the exception object i get is the one that occurs during the forward.
Update:
I found that a errorPath property can be configured to point to a Error Handling Service. If it is not configured, Zuul by default looks for a service named /error and tries to dispatch to that service. Since we did not have any service for /error , the dispatcher.forward() was throwing error.
Question
How can we skip this fwd to an error handling service ? We have a ErrorFilter to log the error. We do not want to have a error handling service.
We had faced the same issue and there is a simple solution to fix the Undertow "eating" the original exception, following my blog post:
http://blog.jmnarloch.io/2015/09/16/spring-cloud-zuul-error-handling/
You need to set the flag allow-non-standard-wrappers to true. In Spring Boot this is doable through registering custom UndertowDeploymentInfoCustomizer. Example:
#Bean
public UndertowEmbeddedServletContainerFactory embeddedServletContainerFactory() {
UndertowEmbeddedServletContainerFactory factory = new UndertowEmbeddedServletContainerFactory();
factory.addDeploymentInfoCustomizers(new UndertowDeploymentInfoCustomizer() {
#Override
public void customize(DeploymentInfo deploymentInfo) {
deploymentInfo.setAllowNonStandardWrappers(true);
}
});
return factory;
}
Now regarding the question, either way I would highly encourage you to implement your own ErrorController, because otherwise you may experience odd Spring Boot behaviour (in our setup - ralying on the default was always generating the Whitelabel error page with 200 HTTP status code - which never happens on Tomcat in contradiction) and in this way was not consumable by AJAX calls for instance.
Related Github issue: https://github.com/spring-cloud/spring-cloud-netflix/issues/524

How to test synchronization between rails application and mobile client?

We need to be sure that our web application and mobile client are communicating correctly.
There is two-side communications from the server (Rails application with rspec testing) to the mobile client (Ruby application, has mspec testing framework) and from the mobile client to the server.
So to be sure that the synchronization mechanism is working as expected we need to test the following things:
Server prepares the data correctly.
Mobile client requests and gets
correct data.
Mobile client
prepares
the data to be sent to the server
correctly.
Server recieves and
parses the correct data from the
mobile client.
Servers sends
response to mobile client that
everything is ok.
Mobile client
should carry out appropriate actions
on the device.
How to test this in isolation?
As for all tests, don't plan for the unexpected. Start with what you know. The unexpected will rear it's ugly head soon enough to tell you what else you should test.
What you have is actually simple to test if you break it apart. Here is my approach:
public final static String SERVER_DATA = "Prepared data from the server";
#Test
public void testServerPreparesDataCorrectly() throws Exception {
... usual setup ...
String actual = server.handleRequest( CLIENT_REQUEST );
assertEquals( SERVER_DATA, actual );
}
public final static String CLIENT_REQUEST = "...";
#Test
public void testClientRequest() throws Exception {
... usual setup ...
String actual = client.getRequestData(...);
assertEquals( CLIENT_REQUEST, actual );
}
#Test
public void testClientResponseProcessing() throws Exception {
... usual setup ...
client.parseServerResponse( SERVER_DATA );
... verify client state ...
}
and so on. The basic idea is to put the input and output of each process step into a constant, then run the code which implements the process step for each expected input and validate the output. Where most outputs are also inputs for other tests.
If something changes, you update the inputs/outputs accordingly. Run the tests. And the failures will tell you which process steps you have to update.

Resources