Stateless EJB isn't aware of roles declared in web.xml - glassfish

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.

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.

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.

Related

Jetty: load a server bean from war before initialization

I have a webapp being loaded from a Jetty server.
I want to load a bean implementing a MappedLoginService inside the webapp (not from the server) before the webapp starts loading.
Is there any way?
I tried to load the bean in the webapp post-construct and in webapp application class (javax.ws.rs.Application), without success.
Put the initialization in the WEB-INF/jetty-web.xml file.
That will load it during WebApp initialization, before the Servlet layer initialization kicks off.
I follow Joakim answer creating a WEB-INF/jetty-web.xml file and it worked. I just want to extend the answer adding a sample jetty-web.xml that prints something before servlet is initiated:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<Configure id="context" class="org.eclipse.jetty.webapp.WebAppContext">
<Get class="java.lang.System" name="out">
<Call name="println">
<Arg>Hello world!</Arg>
</Call>
</Get>
</Configure>
More information about jetty-web.xml syntax here.

Wildfly 17 Elytron: server side authentication with classes from EAR

We plan to migrate from Picketbox to Elytron and face the following problem:
With Picketbox a custom login module can use functionality of (or even can reside in) a deployment module (e.g. an EAR in wildfly/standalone/deployments) to implement authentication on the server side:
<subsystem xmlns="urn:jboss:domain:security:2.0">
<security-domains>
...
<security-domain name="MyDomain" cache-type="default">
<authentication>
<login-module name="MyLoginModule" code="de.example.wildfly.MyLoginModule" flag="required" module="deployment.de.example.wildfly.login"/>
</authentication>
</security-domain>
My first try was to use a custom realm in Elytron. But as far as I understand, a custom realm needs to be a "static" module (meaning it is located under wildfly/modules/...) and thus cannot access "dynamically" deployed modules (see https://developer.jboss.org/message/984198#984198).
<subsystem xmlns="urn:wildfly:elytron:7.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<security-domains>
<security-domain name="MyDomain" default-realm="MyRealm" permission-mapper="default-permission-mapper">
<realm name="MyRealm" role-decoder="from-roles-attribute" />
</security-domain>
</security-domains>
<security-realms>
...
<custom-realm name="MyRealm" module="de.example.wildfly.login" class-name="de.example.wildfly.MyCustomRealm" />
(I omitted some more of the security domain configuration)
When I try to load a Spring context (that is located in an EAR in order to access some custom classes from the EAR) in MyCustomRealm, I get the following error:
org.springframework.beans.factory.access.BootstrapException: Unable to initialize group definition. Group resource name [classpath:applicationContext-appServerBase.xml], factory key [applicationContextEjb]; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from class path resource [applicationContext-appServerBase.xml]; nested exception is java.io.FileNotFoundException: class path resource [applicationContext-appServerBase.xml] cannot be opened because it does not exist
Which is no surprise, because my realm does not depend on the ear or any jar from therein, where the application context is located.
How can authentication (specifically for EJB calls) be customized on server side by using classes from a deployment module (EAR) in Elytron?
Maybe https://github.com/AntonYudin/wildfly-securityrealm-ejb is exactly what you are looking for.
It creates a SecurityRealm that can be configured with the address of an EJB that's deployed with your application.
The EJB has to be Stateless and must implement the method Map<String, Object> authenticate(String, String) which is called with a username and a password.
I guess you have to return a map that contains all roles and groups the user belongs to or null if the credentials are invalid.

unable to set security domain via jboss-web.xml

We have an app that is being deployed as an ear. Within this ear, there is a war that needs to use a specific security domain.
To achieve this, we have configured the standalone-full-ha.xml with the following security section
<security-domain name="ourDomain" cache-type="default">
<authentication>
<login-module code="blah.blah.OurDomain" flag="required" />
</authentication>
</security-domain>
ear/war/WEB-INF/jboss-web.xml is configured as follows:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<security-domain>Quark</security-domain>
<disable-audit>true</disable-audit>
</jboss-web>
With this configuration, the app tries to authenticate against the "other" domain which is there in JBoss by default.
log entries as follows:
TRACE [org.jboss.security] (http-/127.0.0.1:8080-6) PBOX000224: End getAppConfigurationEntry(other), AuthInfo: AppConfigurationEntry[]:
[0]
LoginModule Class: org.jboss.as.security.remoting.RemotingLoginModule
ControlFlag: LoginModuleControlFlag: optional
Options:
name=password-stacking, value=useFirstPass
[1]
LoginModule Class: org.jboss.as.security.RealmDirectLoginModule
ControlFlag: LoginModuleControlFlag: required
Options:
name=password-stacking, value=useFirstPass
When trying to define this as part of the ear in ear/META-INF/jboss-app.xml, that made the whole thing blow up pretty spectacularly - so figured that was less likely to be the way to resolve this.
If the default security domain is changed to ourDomain however, it all works as expected.
This doesn't seem to be a big deal - however, it feels better to be able to leave as much of the configuration in the app as possible.
Any pointers to resolve this appreciated.
Your security domain name specified in jboss-web.xml needs to match the name of some security domain in your JBoss config, in your case the web descriptor specifies Quark while the security subsystem defined domain named ourDomain.
Whenever JBoss can not find the security domain you request in your jboss-web.xml, it will fallback to the default security domain, which in case of 7.x is named other.

org.apache.commons.dbcp.SQLNestedException on tomcat7

I've been fighting with my tomcat server for a while now and it still doesn't work.
I'm using tomcat7, on Debian Wheezy, with PostgreSQL v9.1.
I tried everything possible, and here is what I'm keeping on getting :
database.DataBaseException: Database error while trying to get a new connection. Error information: org.apache.commons.dbcp.SQLNestedException: Cannot create JDBC driver of class '' for connect URL 'null'
database.Connection$.getConnection(Connection.scala:20)
database.Users$.createRegisteredUser(Users.scala:58)
controllers.page.CreateAccountController.before(CreateAccountController.scala:36)
controllers.page.AbstractPageController.processRequest(AbstractPageController.scala:52)
controllers.page.AbstractPageController.doPost(AbstractPageController.scala:79)
javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
I have access to the application pages when I'm running my application and my tomcat server, so far I configured tomcat well enough for this, but as soon as I'm having an action that involves a request to the database (creation of user, etc.), I get this error !
As for the general context of the web application, I've started helping on an already existing project, I installed tomcat7 and cloned the source on my laptop. It works well with the other people working on it - and I haven't made any change to the application code so far (the code works fine): so the error definitely comes from my tomcat configuration and not from the code.
To configure a Tomcat7 server for a web application, what we have to do to configure is creating a context.xml in the META-INF/ directory right ?
I've also added a for the data source to the WEB-INF/web.xml file but it doesn't seem to be mandatory to have a tomcat7 application data source work fine.
I've copied and past my META-INF/context.xml file to Catalina/localhost/ directory with .xml.
I've added the postgres jdbc jar to /usr/share/tomcat7/lib (I've seen a lot of forum posts talking about a conf/lib but I can't find it so I figured it's this lib)
I've read things about tweaking with the tomcat context.xml file and whatnot, but there are pros and cons, and anyway it doesn't work either for me.
Here is my context.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource name="jdbc/dbname"
auth="Container"
type="javax.sql.DataSource"
username="user1"
password="user1"
driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://localhost:5432/dbname"
maxActive="8"
maxIdle="4"/>
<Environment name="encryptionKey" type="java.lang.String" value="<the key>" override="false"/>
</Context>
What I added to the WEB-INF/web.xml:
<resource-ref>
<res-ref-name>jdbc/dbname</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
The code sample where it all starts (application written in scala) :
private val ds: DataSource = new javax.naming.InitialContext().lookup("java:comp/env/jdbc/dbname").asInstanceOf[DataSource]
private var connec: java.sql.Connection = null
def getConnection(): java.sql.Connection = {
try {
if (connec == null) {
connec = ds.getConnection() //here is raised the error
connec.setAutoCommit(false)
}
connec
} catch {
case ex: SQLException => throw new DataBaseException("Database error while trying to get a new connection. Error information: " + ex.toString())
}
}
I tried to run the application without context.xml and without the copy of if in the Catalina/localhost directory, and I get the same error. I guess the server just doesn't find my context.xml file ?
The last thing is I've compared my context.xml with some other people from the project, and they have exactly the same - and it works for them (except the db name and password that depends on what they chose)....
I also made sure the port 5432 is the right one, and it is.
Yes, it's pretty puzzly !
If you want more precision (I might be missing relevant things) don't hesitate to ask me !

worklight http adapter and NTLM authentication

i'm trying to implement NTLM authentication in a Worklight HTTP Adapter in order to connect to M$ back-end servers, such as Sharepoint Web services.
i've set-up my adapter.xml file with <ntlm>.
The adapter.xml structure first version was:
<authentication>
<ntlm />
<serverIdentity>
<username>user</username>
<password>password</password>
</serverIdentity>
</authentication>
My tests are done locally with Worklight studio, i get the following issues:
1) error when invocating WL procedure:
Procedure invocation failed:Could not resolve placeholder 'local.hostname'
where do i have to put this 'local.hostname' setting?
2) i tried to specifiy the hostname property of ntlm tag as given in documentation (IBM infocenter), WL Studio says that the xml is bad formed.
<authentication>
<ntlm hostname="myComputer.intranet.com"/>
<serverIdentity>
<username>user</username>
<password>password</password>
</serverIdentity>
</authentication>
Where "myComputer.intranet.com" is my computer's name within my corporate network.
Attribute 'hostname' is not allowed to appear in element 'ntlm'
Response from IBM Service Request:
The username used to authenticate with NTLM-enabled back-end system must be left padded with the windows domain name followed by a \, and the username.
<serverIdentity>
<username>domain\user</username>
<password>password</password>
</serverIdentity>
This works with hard-coded serverIdentity feature.
Due to security governance in my company, there can't be "generic" server identity. So i have to forward the end users' credentials to back-end systems to authenticate.
How to do this with Worklight authentication mecanisms (adapter-based for instance), where can i set the domain for my username?
Can i mix several kinds of security realms depending on adapters?