I've found what looks to be a mis-feature with JAAS authentication for EJB's.
I'm using the ServerServlet to call EJB's over http/s. It looks like in order
to work, the JAAS realm that authenticates the EJB user must either be named
the same as the WAR that contains ServerServlet OR it must be defined globally
in Tomcat's server.xml.
Defining the JAAS realm in ServerServlet's WAR with any name other than the
WAR's name fails with a strange, "java.rmi.RemoteException: Received invalid
response code from server: 3". Best I can tell, the '3' corresponds to
org.apache.openejb.client.ResponseCodes.AUTH_DENIED, which for some reason gets
marshalled back under a "javax.ejb.EJBException: Unknown Container Exception"
with a cause of that RemoteException. Possibly there's another problem here
with marshalling back the error condition from an AUTH_DENIED, but I haven't
dug into that.
The closest thing to a root cause I can find is the behavior in
org.apache.tomee.catalina.TomcatSecurityService::findRealm which is called by
login, ultimate called by org.apache.openejb.server.ejbd.EjbRequestHandler
processRequest method (securityToken =
securityService.login(authentication.getRealm(), authentication.getUser(), new
String(authentication.getPassword())); // Line 171)
The findRealm method searches a Map from TomcatWebAppBuilder which appears to
contain keys of the various archive names to their defined realms.
The two ways that this works successfully are:
1) If the Realm that should be securing your EJB's is in the default
conf/server.xml, the fallthrough that returns defaultRealm works.
2) If the Realm is named the same as the WAR's URI, then it'll be found in the
Map within TomcatWebAppBuilder.
My (failing) environment looked like this:
ejb.war!META-INF/context.xml:
<Context>
<Realm
className="org.apache.catalina.realm.JAASRealm"
appName="ucs_security"
userClassNames="ucs.common.tomee.UcsUser"
roleClassNames="ucs.common.tomee.UcsRole"
/>
</Context>
ejb.war!WEB-INF/web.xml:
<web-app version="3.1"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
<servlet>
<servlet-name>ServerServlet</servlet-name>
<servlet-class>org.apache.openejb.server.httpd.ServerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServerServlet</servlet-name>
<url-pattern>/invoke/*</url-pattern>
</servlet-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>ejb</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>ejb-user</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<security-role>
<role-name>ejb-user</role-name>
</security-role>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>ucs_security</realm-name>
</login-config>
</web-app>
And the client-side JAAS configuration also uses "ucs_security" as its realm
name.
If I moved the <Realm/> tag from the WAR's context.xml into server.xml, EJB
calls authenticated successfully as it become the defaultRealm. That bound the
entire server to that authentication realm which isn't desired. If I rename
all the "ucs_security" references to "ejb" (or presumably renamed the WAR
itself to "ucs_security" though I didn't test that), then it finds the right
realm & authenticates.
It seems like incorrect behavior to force the name of the authentication realm
to be the name of the EAR with all of the other various parts of JAAS able to
specify an arbitrary realm name.
Is this expected behavior, or have I done something silly I'm not seeing? This
is all with TomEE 7.0.1.
Thanks in advance,
Zac Bedell