Caricard
Caricard

Reputation: 391

Jersey custom SecurityContext on EJB jax-rs resource

I am trying to implement my own ContainerRequestFilter and configure SecurityContext. It works well on jax-rs resources but EJB jax-rs throws javax.ejb.AccessLocalException

Only relevant thing I found is 4 years old and the workaround doesn't seem pretty. https://java.net/projects/jersey/lists/users/archive/2010-05/message/265

My custom SecurityContext:

@Provider
@PreMatching
public class SecurityFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext filterContext) throws IOException {
        filterContext.setSecurityContext(new Authorizer());
    }

    public class Authorizer implements SecurityContext {

    public Principal getUserPrincipal() {
        return null;
    }

    public boolean isUserInRole(String role) {
        return true;
    }

    public boolean isSecure() {
        return false;
    }

    public String getAuthenticationScheme() {
        return null;
    }
}

Tested resource (works without @Stateless)

@Path("test")
@Stateless
public class TestSecureResource {

    @GET
    @RolesAllowed("admin")
    @Path("admin")
    public Response secureTest() {
        return Response.status(200).entity("admin").build();
    }

}

Does someone know how to make this work?

Upvotes: 4

Views: 6117

Answers (3)

shamoh
shamoh

Reputation: 164

You can use JAX-RS SecurityContext as an API not SPI. It is uncommon for an application developer to provide a SecurityContext implementation. If you do you have to know that it has only "local JAX-RS validity" since it is a JAX-RS specific API. Neither Servlet/Web container nor EJB container work with it. They don't have to as Java SE and EE have more general security support.

If you want your security checks to works in a Java EE application (i.e. HttpServletRequest.isUserInRole(...), EJBContext.isCallerInRole(...) or javax.annotation.security annotations on EJBs) you need to secure your Servlet layer using Java EE features. This means to use for example <security-constraint> in web.xml. You can use * as <role-name> meaning "all authenticated" user can call the REST API:

<security-constraint>
    <web-resource-collection>
        <url-pattern>/rest/admin/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>adminRole</role-name>
    </auth-constraint>
</security-constraint>
<security-constraint>
    <web-resource-collection>
        <url-pattern>/rest/orders/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>*</role-name> <!-- all authenticated users -->
    </auth-constraint>
</security-constraint>

When your Java EE application is secured as shown above we can enable javax.annotation.security annotations in JAX-RS using the Jersey-specific feature called RolesAllowedDynamicFeature.

Register the feature:

@ApplicationPath("/rest")
public class MyApplication extends ResourceConfig {
    public MyApplication() {
        super(AdminResource.class);
        register(RolesAllowedDynamicFeature.class);
    }
}

Secure your resources:

@Path("/admin")
@RolesAllowed("adminRole")
public class AdminResource {
    @GET
    public String get() { return "GET"; }
    ...
}

See Jersey User guide for more details about securing JAX-RS applications.

So you were close. You don't need to implement a SecurityContext yourself. You must not implement it if you deal with secured EJBs. And finally you need to secure your JAX-RS layer as common Web/Servlet application. I'm sure you already have secured your Web/HTML pages.

Upvotes: 6

Andrew
Andrew

Reputation: 531

I had the same issue Richard, I followed the Jersey security guide below. https://jersey.java.net/documentation/latest/security.html#d0e10816

I used the ContainerRequestFilter to authenticate, here I'd set a custom implementation of SecurityContext if the authentication was successful which the rolesalloweddynamic feature would use along with the rolesallowed annotations to authorise access to a specific resource. These three components allowed me to authenticate and authorise on an application level, not on a container level.

This worked great until my application was converted from a servlet to a EJB/servlet (I added a stateless ejb annotation to a jax-rs resource class). EJB uses the rolesallowed annotation to restrict access to its bean methods at a container level, therefore it conflicted with my application level authentication/authorisation.

I'm still searching for a comphrensive solution, even if it's disabling EJB level method security so I can leave it to the ContainerRequestFilter to authenticate and the rolesalloweddynamicfeature to authorise. Here is my post on the issue.
Client not authorized for this invocation JAX-RS EJB error

Upvotes: 0

Caricard
Caricard

Reputation: 391

I finally resolved it. I could not use javax.security.RolesAllowed since it is handled differently from EJB container. So I implemented my own RolesAllowed annotation. It has a disadvantage that it is not so advanced as implementation of original javax.security.RolesAllowed. For example it does not support entity filtering like it is described in here. There is a demand from Java EE comunity for integration of security in next Java EE 8 release and I hope we'll see better solution.

I put the working example on github.

Upvotes: 1

Related Questions