How to mock a SecurityContext

java, jax-rs, jersey-2.0, mocking, unit-testing

Solution

Disclaimer: I'm not really a Mockito user, but from what I understand, mocking is used for situations where you have injected class dependencies (fields), and you mock those dependencies. In which case you still need to set the field with the mocked object. For example

public class TestClass {
    TestService testService;
    public void doTest() {
        System.out.println(testService.getString());
    }
    public void setTestService(TestService testService) {
        this.testService = testService;
    }
}
public class TestService {
    public String getString() {
        return "Hello world";
    }
}
@Test
public void toTest() {
    TestService testService = Mockito.mock(TestService.class);
    Mockito.when(testService.getString()).thenReturn("Hello Squirrel");
    TestClass testClass = new TestClass();
    testClass.setTestService(testService);
    testClass.doTest();
}

You can see we are setting the the `TestService` in the `TestClass` with the mocked object. It's not greatest example, as we could simple instantiate `TestService`, but it shows, from my understanding, how the mocking should work.

That being said, I don't see how it is possible to do this with the `AuthorizationRequestFilter`, as it's handled by the test container, and we are not instantiating it for a unit test. Even if we were, it would seem intrusive (and redundant) to add a `SecurityContext` field.

So without a full integration test, where we are starting the server, and using the server's authentication capabilities, it will be difficult to handle the `SecurityContext` per this use case, as the `SecurityContext` is created by the container, taking information from the underlying servlet containers authentication mechanism.

One way you can achieve this though (which IMO doesn't seem very elegant - but works), without a full integration test, is to create a a filter which performs before your `AuthorizationRequestFilter`, and set the `SecurityContext` from there. Testing aside, this is actually pretty common in cases where we need to implement outr own custom authentication mechanism.

An example of how you might do this for your unit test, might be something like:

public class UrlerResourceTest extends JerseyTest {
    ...
    @Override
    public Application configure() {
        return new ResourceConfig(FooResource.class)
                .register(AuthorizationRequestFilter.class)
                .register(AuthenticationFilter.class);
    }

    @Provider
    @Priority(Priorities.AUTHENTICATION)
    public static class AuthenticationFilter implements ContainerRequestFilter {
        @Override
        public void filter(ContainerRequestContext requestContext) throws IOException {
            requestContext.setSecurityContext(new SecurityContext() {
                @Override
                public Principal getUserPrincipal() {
                    return new Principal() {
                        @Override
                        public String getName() {
                            return "Stackoverflow";
                        }
                    };
                }
                @Override
                public boolean isUserInRole(String string) {
                    return "privileged".equals(string);
                }
                @Override
                public boolean isSecure() { return true; }
                @Override
                public String getAuthenticationScheme() { return "BASIC"; }                
            });
        }  
    }
    ...   
}

This filter will perform before the `AuthorizationRequestFilter` because of the `@Priority` annotation. We've set it to `Priorities.AUTHENTICATION` which will before before any other filter without such annotation. (See Priorities API and Priorities with Jersey. Also the `SecurityContext` will be passed along between filters and also be injected into your resource class.

As I said, I don't think this is very elegant to have to create another filter, but it works for this purpose. Also I am not too familiar with the Jersey Test Framework, as I'm still beginning with it, but there are many configuration options for deployment within a servlet context. I don't know if we can configure the needed authentication mechanism for this case, but it might be something worth looking into.

Edit: In the beginning I explained about setting the field for the test object, but we can also pass the mocked object to a method. For example we could mock the `ContainterRequestContext` in the `filter` method, and call `filter` ourselves, passing the mocked `ContainerRequestContext`. But this is only useful when we are actually unit testing the filter class and instantiating it ourselves, which is not the case here.

Problem

Endpoints with Jersey. I want to secure an endpoint with a `ContainerRequestFilter` ``` @Provider @Secured public class AuthorizationRequestFilter implements ContainerRequestFilter { @Override public void filter(ContainerRequestContext requestContext) throws IOException { final SecurityContext securityContext = requestContext.getSecurityContext(); //TODO: on logger here... System.out.printf("Filtering %s request... AuthorizationRequestFilter\n", requestContext.getMethod()); requestContext.getHeaders().add("X-Secured-By", "Jersey >_<"); System.out.printf("SecurityContext: %s (%s).\n", securityContext, securityContext.getAuthenticationScheme()); if (securityContext == null || !securityContext.isUserInRole("privileged")) { requestContext.abortWith(new UnauthorizedResponse().getResponse()); } } } ``` The annotation `@Secured`: ``` @NameBinding @Retention(RetentionPolicy.RUNTIME) public @interface Secured {} ``` So I can do this: ``` @Path("foobar") public class FooResource { //... @Context SecurityContext securityContext; //... @GET @Secured @Path(value = "foo") @Produces(MediaType.APPLICATION_JSON) public Response getFoo(@Context SecurityContext sc, @Context UriInfo ui, @Context HttpHeaders hh) { // ... } //... ``` And I'm doing it right (I think), because with my test I don't even pass through the `getFoo` endpoint but is the ContainerRequestFilter that kicks me out. In fact I receive this (the "X-Secured-By" header is hand-made): ``` Headers: {X-Secured-By=[Jersey >_< kicked you out!], Content-Length=[97], Date=[Wed, 03 Dec 2014 17:46:50 GMT], Content-Type=[application/json], X-Powered-By=[Jersey ^_^]} Response: InboundJaxrsResponse{ClientResponse{method=GET, uri=http://localhost:9998/urler/test, status=401, reason=Unauthorized}} ``` Now it would be nice to mock the `SecurityContext`. This is what I'm doing... and if I'm here, it's obviously silly and/or wrong. ``` public class UrlerResourceTest extends JerseyTest { //.... @Override public TestContainerFactory getTestContainerFactory() { GrizzlyTestContainerFactory grizzlyTestContainerFactory = new GrizzlyTestContainerFactory(); System.out.printf("The GrizzlyTestContainerFactory: %s ", grizzlyTestContainerFactory); // just for debugging... return grizzlyTestContainerFactory; } @Test public void testSecuredEndpoint() throws JSONException { SecurityContext securityContext = Mockito.mock(SecurityContext.class); Mockito.when(securityContext.isUserInRole(anyString())).thenReturn(true); Mockito.when(securityContext.getAuthenticationScheme()).thenReturn("Just Mocking..."); ReflectionTestUtils.setField(resource, "securityContext", securityContext, SecurityContext.class); final Response response = target("foobar") .path("foo") .request(MediaType.APPLICATION_JSON) .get(); System.out.println(getFormattedStringResponseInfo(response)); JSONObject entity = new JSONObject(response.readEntity(String.class)); assertTrue(entity.get("secured").equals(true)); assertTrue(response.getHeaders().containsKey("X-Secured-By")); assertEquals(Status.OK.getStatusCode(), response.getStatus()); } ``` How can I mock the `SecurityContext` in my tests? Thank you so much in advance.

Original source