Skip to main content

"Security context" not propagated to Interceptors?

8 replies [Last post]
Anonymous

Hi.
I am learning EJB3 and Glassfish, so I am writing this silly code every now and then. This time I came across sth strange to me.
I configured two usernames on the server using the web console: "szczyp" in group "szczyp", and "guest" in group "guest". I also ticked the "Default Principal To Role Mapping". I am using the default file realm.
I have a web and ejb module deployed on the server. Both modules have the roles and security restrictions specified: the web module in web.xml, and ejb module with annotations, which require the user to be of role "szczyp" to be able to access the resources.
The ejb has a single interceptor.
In each of the resources (servlet, interceptor, ejb) I call similar code to retrieve the username / principal name and check if the user / caller is in the role I am expecting him/her to be ("szczyp" in this case, since this is the only role that can proceed that far due to security restrictions). Also, the ejb is called from the servlet.
The thing is that for the servlet and ejb the code works as expected, but for the interceptor, the principal name is the expected one, but the injected EJBContext.isCallerInRole("szczyp") returns false. Please see the code snippets:

the web.xml security settings:

</p>
<p>    guest</p>
<p>    szczyp</p>
<p>        Whole app<br />
        /*</p>
<p>        szczyp</p>
<p>    BASIC<br />
    Test</p>
<p>

the servlet:

<br />
@EJB<br />
private Greeter greeter;</p>
<p>@Override<br />
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {<br />
    response.setContentType("text/plain");<br />
    PrintWriter out = response.getWriter();<br />
    out.println("Servlet:");<br />
    out.println("\tUsername: " + request.getUserPrincipal().getName());<br />
    out.println("\tIs user in role [szczyp]: " + request.isUserInRole("szczyp"));<br />
    out.println();<br />
    out.print(greeter.greet(null));<br />
}<br />

the ejb:

<br />
@Remote<br />
public interface Greeter {</p>
<p>    String greet(String s);<br />
}</p>
<p>----------------------------------------------------------</p>
<p>@Interceptors(TestInterceptor.class)<br />
@DeclareRoles( { "szczyp", "guest" })<br />
@RolesAllowed("szczyp")<br />
@Stateless<br />
public class GreeterBean implements Greeter {</p>
<p>    @Resource<br />
    private EJBContext context;</p>
<p>    @Override<br />
    public String greet(String s) {<br />
        StringBuilder sb = new StringBuilder();<br />
        if (s != null) {<br />
            sb.append(s).append("\n");<br />
        }<br />
        sb.append("EJB:\n");<br />
        sb.append("\tUsername: " + context.getCallerPrincipal().getName()).append("\n");<br />
        sb.append("\tIs caller in role [szczyp]: " + context.isCallerInRole("szczyp")).append("\n");<br />
        return sb.toString();<br />
    }</p>
<p>}<br />

the interceptor:

<br />
public class TestInterceptor {</p>
<p>    @Resource<br />
    private EJBContext context;</p>
<p>    @AroundInvoke<br />
    public Object doStuff(InvocationContext ctx) throws Exception {<br />
        StringBuilder sb = new StringBuilder("EJB interceptor:\n");<br />
        sb.append("\tUsername: " + context.getCallerPrincipal().getName()).append("\n");<br />
        sb.append("\tIs caller in role [szczyp]: " + context.isCallerInRole("szczyp")).append("\n");<br />
        ctx.setParameters(new String[] { sb.toString() });<br />
        return ctx.proceed();<br />
    }<br />
}<br />

The result is as follows:

<br />
Servlet:<br />
	Username: szczyp<br />
	Is user in role [szczyp]: true</p>
<p>EJB interceptor:<br />
	Username: szczyp<br />
	Is caller in role [szczyp]: false</p>
<p>EJB:<br />
	Username: szczyp<br />
	Is caller in role [szczyp]: true<br />

As you can see, the interceptor reports false for the caller-in-role test.
Is this the correct behavior, or is this a bug, or I simply don't know of something obvious?

Second question: are the roles declared using the @DeclaresRoles annotation global for the whole ejb module, or local to the ejb method / class they are declared for?

Thanks.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
nitkal
Offline
Joined: 2008-10-22

This issue has been now fixed. Thanks..

https://glassfish.dev.java.net/issues/show_bug.cgi?id=6800

--Nithya

nitkal
Offline
Joined: 2008-10-22

Is this after configuring the element with the sun-ejb-jar.xml? This should map the role to the propagated principal in the EJB and the interceptor and ensure that the caller's role is present in the context.

--Nithya

szczyp

Hi. Good to hear from someone, monologue is boring ;-)
I tried two approaches:
* I checked the "Default Principal To Role Mapping", so that I don't have to do the mapping myself in the sun-xxx.xml files
* I unchecked this setting, and provided sun-application.xml file in the EAR, as I am packaging the application in ear and deploy it using this (well, mainly I invoke the application from eclipse, using the TestEAR project, but I also double checked and deploying the application outside of eclipse)

I don't think mapping roles to users is a problem, if you've read the whole topic, you must have seen that sometimes it works correctly, sometimes it doesn't.
Of course, I know little of this, so I may be wrong, but then again - it does work most of the time, just not when a servelt invokes an ejb that has an interceptor.
To make sure, just download the source / build package from my most recent posts, the output generated by the servlet (mapped to /test) will tell you everything.

szczyp

szczyp

To this post I am enclosing the short summary of my debug session, which points out why I think this happens. I couldn't add it to the previous post as there is a two file limit.
Cheers.

szczyp

Hi again.
I changed my source code to do the following:
1. the servlet invokes GreeterBean
2. the bean has an interceptor, so it is invoked first, and isCallerInRole("szczyp") returns false, even though I logged in as "szczyp" and need this role to access the servlet and ejb in the first place
3. then the bean itself is invoked, and isCallerInRole("szczyp") returns true, as expected
4. then, the invoked bean calls another injected bean (GreeterBean2 instance)
5. GreeterBean2 has the same interceptor as GreeterBean, and this time isCallerInRole("szczyp") returns true!

I repeat the call from the servlet, to make sure it behaves like this the first and more times when invoked. So the result is: when the interceptor is called before a bean called directly from a servlet, the isCallerInRole() test fails. When it is called from a bean that was called from another bean, it works as expected.

So, I downloaded the sources for glassfish, and did some debugging. I enclose the result for isCallerInRole() for the interceptor called from the servlet in this post, please see the attached files.

What I noticed is this:

there is a class "com.sun.enterprise.security.provider.BasePolicyWrapper"
and deep into isCallerInRole() the doImplies(ProtectionDomain, Permission) method of the mentioned class is invoked - line 378 of BasePolicyWrapper.java
Within this method, the Permission parameter is of type "javax.security.jacc.EJBRoleRefPermission", for bean GreeterBean, role szczyp
and some variables are initialized to perform the check:
* contextId of the invocation, which is set to "TestEAR/TestWeb_war"
* PolicyConfigurationImpl, with a property which is the URL to a policy file it reads, and this points to the incorrect one I think: "file:/home/rafal/Apps/glassfish-v2/domains/szczypior.eu/generated/policy/TestEAR/TestWeb_war/granted.policy"
So as a result the Policy object has permissions that reflect the policy file for web module (WebRoleRefPermissions for the servlets and jsp and so on), and is looking for EJBRoleRefPermission, fails, and returns false.

When I follow the same path for the bean itself, or the same interceptor, but when a bean is called from another bean, the contextId is "TestEAR/TestEJB_jar", and the URL to the policy file are set to "file:/home/rafal/Apps/glassfish-v2/domains/szczypior.eu/generated/policy/TestEAR/TestEJB_jar/granted.policy", and the isCallerInRole() returns true, as the requested permission can be found there.

So, is this a bug of glassfish, or is it valid behaviour I am experiencing?

To give more information, I am running Glassfish 2 update 2 on Sun Java 6 update 10, on 64bit Kubuntu 8.04.

Please take a look at the files I attached to this post. These contain the source for the web, ejb and ear, and also a TestEAR,ear file to deploy and run.

ksak
Offline
Joined: 2005-05-20

This does sound like a bug. Can you please file an issue against the ejb container. Please include all of this detailed analysis. Thanks.

https://glassfish.dev.java.net/servlets/ProjectIssues

szczyp

Done: https://glassfish.dev.java.net/issues/show_bug.cgi?id=6800
If you need more information and maybe some help, please tell me, I am willing to help within my capacity.
Cheers.

szczyp

Bump.
If anyone is interested, please take a look at the pack.tgz file I've attached, it contains eclipse 3.4.1 projects with the code in question. The only change I've made is that I explicitly mapped principal-names to role-names, so that the "Default principal to role mapping" doesn't need to be checked. It didn't change the behavior.
Cheers.