Question

I'm actually running into a role propagation problem and I need help. I am using Glassfish 4.0 and I'm deploying a war containing a JAX-RS resource and an EJB with a remote and a local view for testing purposes.

I have declared roles in my web.xml and glassfish-web.xml Deployment Descriptors, linked to a File Realm in Glassfish. Those roles are correctly used by the JAX-RS resource, but the EJB doesn't seem to see them.

I will show you the files I'm using, and then the results of the different call outputs I've tested so far.

TL/DR : Sorry for the extremely long post. Please go to PART II / Test 2

PART I : The code

The glassfish-web.xml Deployment Descriptor

<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app>
    <context-root>/war-test-4</context-root>
        <security-role-mapping>
        <role-name>test</role-name>
        <group-name>test</group-name>
    </security-role-mapping>
    <security-role-mapping>
        <role-name>test2</role-name>
        <group-name>test2</group-name>
    </security-role-mapping>
    <security-role-mapping>
        <role-name>authenticated</role-name>
        <group-name>authenticated</group-name>
    </security-role-mapping>
</glassfish-web-app>

The web.xml Deployment Descriptor

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    id="WebApp_ID" version="3.1">
    <display-name>war-test-4</display-name>
    <servlet>
        <description>JAX-RS Tools Generated - Do not modify</description>
        <servlet-name>javax.ws.rs.core.Application</servlet-name>
        <load-on-startup>1</load-on-startup>
        <security-role-ref>
            <description>Test</description>
            <role-name>test</role-name>
            <role-link>test</role-link>
        </security-role-ref>
        <security-role-ref>
            <description>Auth users</description>
            <role-name>authenticated</role-name>
            <role-link>authenticated</role-link>
        </security-role-ref>
        <security-role-ref>
            <description>Test2</description>
            <role-name>test2</role-name>
            <role-link>test2</role-link>
        </security-role-ref>
    </servlet>
    <servlet-mapping>
        <servlet-name>javax.ws.rs.core.Application</servlet-name>
        <url-pattern>/jaxrs/*</url-pattern>
    </servlet-mapping>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Admin Resources</web-resource-name>
            <description>Administration resources</description>
            <url-pattern>/jaxrs/*</url-pattern>
            <http-method>GET</http-method>
        </web-resource-collection>
        <auth-constraint>
            <description>TEST</description>
            <role-name>test</role-name>
            <role-name>test2</role-name>
            <role-name>authenticated</role-name>
        </auth-constraint>
    </security-constraint>
    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>test-realm</realm-name>
    </login-config>
    <security-role>
        <description>Test</description>
        <role-name>test</role-name>
    </security-role>
    <security-role>
        <description>Auth users</description>
        <role-name>authenticated</role-name>
    </security-role>
    <security-role>
        <description>Test2</description>
        <role-name>test2</role-name>
    </security-role>
</web-app>

The stateless EJB SessionBeanTest.java

/**
 * Session Bean implementation class SessionBeanTest
 */
@Stateless(mappedName = "SessionBeanTest")
//@RolesAllowed({"authenticated"})
//@DeclareRoles({"authenticated","test","test2"})
public class SessionBeanTest implements SessionBeanRemote, SessionBeanLocal {

    @Resource
    private SessionContext sessionContext;

    @Override
    public String get() {
        return MessageFormat
                .format("EJB Call by :{0} authenticated? : {1} / test2 ? : {2} / test ? : {3}",
                        sessionContext.getCallerPrincipal().getName(),
                        sessionContext.isCallerInRole("authenticated"),
                        sessionContext.isCallerInRole("test2"), sessionContext.isCallerInRole("test"));
    }
}

My JAX-RS Service AccessTest.java

@Path("access")
//@DeclareRoles({/*"authenticated","test",*/"test2"})
@Stateless
public class AccessTest {

    @Inject
    private SessionBeanLocal testBean;
    @GET
    @Path("1")
    public Response test(@Context HttpServletRequest req){
        return Response.ok(MessageFormat
                .format("JAX-RS Call by :{0} authenticated? : {1} / test2 ? : {2} / test ? : {3}",
            req.getUserPrincipal().getName(),
                    req.isUserInRole("authenticated"),
                    req.isUserInRole("test2"),
                    req.isUserInRole("test"))).build();

    }

    @GET
    @Path("2")
    public Response test2(){
        return Response.ok(testBean.get()).build();
    }
}

As you've probably noticed, I have commented @DeclareRoles and @RolesAllowed annotations in both my EJB and my JAX-RS resource. I also have 2 URIs in my JAX-RS service. One is directly giving the user informations and roles, and the other uses the EJB to retrieve the same information. Both should return the exact same output if a user is logged.

PART II : The tests

Now, using a web-service tester (Paw on Mac, Curl based, very useful!), I'm accessing both URIs:

Test 1 : No user is logged in

Output for URI /jaxrs/access/1 with no user

HTTP/1.1 401 Unauthorized
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition  4.0  Java/Oracle Corporation/1.7)

Output for URI /jaxrs/access/2 with no user

HTTP/1.1 401 Unauthorized
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition  4.0  Java/Oracle Corporation/1.7)

So far, it seems to work as planned, an authenticated user is required to access the resource. But have a look at the second test...

Test 2 : A user with all roles is logged in

Output for URI /jaxrs/access/1 with user having all roles

HTTP/1.1 200 OK
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition  4.0  Java/Oracle Corporation/1.7)

JAX-RS Call by :testadmin authenticated? : true / test2 ? : true / test ? : true

Output for URI /jaxrs/access/2 with user having all roles

HTTP/1.1 200 OK
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition  4.0  Java/Oracle Corporation/1.7)

EJB Call by :testadmin authenticated? : **false** / test2 ? : **false** / test ? : **false**

This is the part I don't understand. The Roles declared in web.xml and glassfish-web.xml aren't propagated to the EJB, which is in the same WAR project.

Test 3 : Uncommenting the @DeclareRoles annotation in code

Whether I uncomment the @DeclareRoles({"authenticated"}) in my EJB or my JAX-RS service, I get the following output :

Output for URI /jaxrs/access/1 with user having all roles, @DeclareRoles uncommented

HTTP/1.1 200 OK
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition  4.0  Java/Oracle Corporation/1.7)

JAX-RS Call by :testadmin authenticated? : true / test2 ? : true / test ? : true

Output for URI /jaxrs/access/2 with user having all roles, @DeclareRoles uncommented

HTTP/1.1 200 OK
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition  4.0  Java/Oracle Corporation/1.7)

EJB Call by :testadmin authenticated? : **true** / test2 ? : **false** / test ? : **false**

Only the roles I declare are seen by the EJB, but the JAX-RS service sees them all

PART III : The Remote EJB call

I also have a pure Java client for testing purposes. Here it is :

public class Main {

    public static void main(String[] args) throws Exception {
        getRemoteService();
    }

    public static void getRemoteService() throws Exception {
        String host = "127.0.0.1";
        String port = "3700";
        Properties props = new Properties();
        props.setProperty("java.naming.factory.initial", "com.sun.enterprise.naming.SerialInitContextFactory");
        props.setProperty("java.naming.factory.url.pkgs", "com.sun.enterprise.naming");
        props.setProperty("java.naming.factory.state", "com.sun.cobra.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
        props.setProperty("org.omg.CORBA.ORBInitialHost", host);
        props.setProperty("org.omg.CORBA.ORBInitialPort", port);

        Context amInitial = null;
        amInitial = new InitialContext(props);
        ProgrammaticLogin programmaticLogin = new ProgrammaticLogin();
        programmaticLogin.login("testuser2", "password");
        SessionBeanRemote service = (SessionBeanRemote) amInitial.lookup("SessionBeanTest");
        System.out.println(service.get());
        programmaticLogin.logout();
        programmaticLogin.login("testadmin", "password");
        System.out.println(service.get());
    }
}

This client uses ProgrammaticLogin interface to log in and use the EJB with CORBA. I'm not planning to use it other than for testing.

First, the client will log in with a limited user, then with a user with all roles. Here are the test results using this Client :

Test 1 : Testing the remote EJB with @DeclareRoles commented

EJB Call by :ANONYMOUS authenticated? : false / test2 ? : false / test ? : false
EJB Call by :ANONYMOUS authenticated? : false / test2 ? : false / test ? : false

Test 2 : Testing the remote EJB with @DeclareRoles({"authenticated","test","test2"}) uncommented

EJB Call by :ANONYMOUS authenticated? : false / test2 ? : false / test ? : false
EJB Call by :ANONYMOUS authenticated? : false / test2 ? : false / test ? : false

Here, the users aren't authenticated either. But when I add a @RolesAllowed annotation to the EJB I get this:

Test 3 : Testing the remote EJB with @DeclareRoles and @RolesAllowed({"authenticated"}) added

EJB Call by :testuser2 authenticated? : true / test2 ? : true / test ? : false
EJB Call by :testadmin authenticated? : true / test2 ? : true / test ? : true

This is all I've tested so far. I don't understand why the roles declared in the Deployment Descriptors are correctly used by the JAX-RS service, but not by the Stateless EJB. I need your help on this, I'm not really into putting boilerplate Roles annotations on every EJB.

Was it helpful?

Solution

I am unaware of either of the EJB 3.2 and Java EE 7 specifications mandating or at least recommending compatible implementations' containers to endorse Security Role declarations of others. On the contrary, what these documents, to my eye, hint, is that such declarations are constrained --at least from the perspective of "userspace" code-- within the operational environment they were defined in.

With regard to your observations:

  • GlassFish is a JSR-115- (JACC-) compatible Application Server. According to §§ 3.1.1 - 3.1.5, §§ 4.2 - 4.3 of that specification, roles declared via web.xml and ejb-jar.xml elements, or equivalent annotations, are to be "translated" into WebRoleRefPermissions and EJBRoleRefPermissions, respectively, by the default JACC provider; they are thus disparate. These and a few other Java EE-specific Permissions are --at least in theory-- accessible from "userspace" code, e.g. as demonstrated here (this approach actually still works for me on GlassFish 4.1).
  • JAX-RS in Java EE --i.e. Jersey in particular-- sits on top of the Servlet infrastructure. This is principally the reason for SecurityContext.isUserInRole(String) and HttpServletRequest.isUserInRole(String) being equivalent in this scenario. For a different hypothetical Servlet-independent implementation, however, this would not have been the case, and it would once again be required to define security roles twice.

In conclusion, I don't consider this behaviour a specs violation. It may be counter-intuitive --but that's a different story.

As an aside, I wholeheartedly agree that the great lack of resources on the platform's security facilities, i.e. JACC and JASPIC in particular, is dispiriting; I would at least expect a dedicated chapter on each to be included in the official tutorial. Were it not for Arjan Tijms's extensive collection of in-depth articles on the subject, many of us would still be left in the dark. One can only hope that the new JSR-375 will help change that --and much more-- without (hopefully!) reinventing the wheel and/or layering everything atop Servlet Filters.

OTHER TIPS

A hint to make your web.xml a bit shorter. The following is not need for the Servlet:

<security-role-ref>
    <description>Test2</description>
        <role-name>test2</role-name>
        <role-link>test2</role-link>
</security-role-ref>

You only should use this if you have a binary Servlet that uses different roles than what your app is using. role-refs already default to the application roles.

Your test looks pretty much like this one: https://github.com/javaee-samples/javaee7-samples/tree/master/jaspic/ejb-propagation

On GlassFish 4.1 (one revision higher than you are using) that test passed if my memory serves me correctly.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top