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.