A RESTful and secure EJB component

Java EE 6 not only allows us to package EJBs into their own module, but also to deploy our Beans directly into the WAR module that will use them. We will see how to secure both of these scenarios.

Bean packaged into the WAR module

Often, we don't need to package Enterprise Beans into a separated module; we can collocate them inside the same WAR client module and simplify our application architecture. Now, we will develop a simple EJB that will be injected into the existing MyProtectedServlet class. We will also see the security context to be passed and the configuration we need to do.

Let's start simple; we can code this really simple Stateless Bean with no interface view, as shown in the following code snippet:

package net.lucamasini.security;

import javax.ejb.Stateless;

@Stateless
public class NoInterfaceBeanInWarModule {
    public String echo(String input) {
        return "$"+input+"$";
    }
}

This Java file must be in the same folder containing our servlet, and we also need to modify the servlet itself to include the injection. Use the following code:

@EJB 
private NoInterfaceBeanInWarModule service;

And add the service invocation as follows:

resp.getWriter().println("echo:"+service.echo("echo"));

Nothing special until now, we can find many tutorials that show us how to do this. If we call the usual URL now, we will see another line into the browser that displays the following:

echo:$echo$

Tip

When do we often change metadata?

In this section, we will often change the annotation for our experiment, so my advice is to launch the deploy goal every time to prevent incurring unexpected results. This will be unnecessary during the standard development lifecycle.

So let's start introducing security into our EJB by adding the following annotation to the Bean class:

@RolesAllowed("my-user")

Now, by calling the URL again we will see the same output, but the Bean is now secured. In fact, every call to any method will check if the principal has the my-user role, and if not a security error will be raised. Let's check this by adding the same annotation to the single method with another role, as follows:

@RolesAllowed("my-special-user")

Now we are not allowed to access the test URL; we will see the following error message on the server log (and also on the browser window):

javax.ejb.EJBAccessException: [EJB:010160]Security violation: User weblogic has insufficient permission to access EJB type=<ejb>, application=chapter3-ear, module=/chapter3-web, ejb=NoInterfaceBeanInWarModule, method=echo, methodInterface=Local, signature={java.lang.String}.

The exception is very clear and tells us that the currently logged-in user is weblogic, and also displays the signature of the method that we are trying to call, to let us quickly identify the problem. To solve it, we can add role mapping to the custom deployment descriptor or use the XACML Role Mapper. In the latter case, we can add the following two lines to the create-roles.py Python file used for our servlet:

xacmlRoleMapper.createRole(None,'my-special-user',None)
xacmlRoleMapper.setRoleExpression(None,'my-special-user', 'Grp(users)')

And then launch the WLST to execute it as follows:

mvn wls:wlst -DfileName=create-roles.py

This fixes the problem and allows us to execute our servlet again. But, we want to go a step further and configure this using a different group and WebLogic's custom deployment descriptor.

The first step is to configure the internal LDAP server, creating a new bookreaders group and then adding a new user, luca, to that group. This can be done with the Admin Console as we have seen in Chapter 2, WebLogic Security Realm. Then we must create the weblogic-ejb-jar.xml file in the WEB-INF folder with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<weblogic-ejb-jar xmlns="http://xmlns.oracle.com/weblogic/weblogic-ejb-jar">
    <security-role-assignment>
        <role-name>my-special-user</role-name>
        <principal-name>bookreaders</principal-name>
    </security-role-assignment>
</weblogic-ejb-jar>

By redeploying our example, we can now successfully call our test URL using the new configured user, luca.

Changing Security Identity with RunAs

There are some use cases where we need to call some business methods without having the right to do so. For instance, you may need to call a method that is usually called by administrative users from a non-administrative account.

The solution for that use case is the RunAs annotation, which allows us to impersonate any principal, because by default Java EE trusts identities between containers and we don't need any special configuration for this to work.

We can see this at work in our application by removing the newly created user/group and any role mapping, and then adding the following annotation in the Bean class definition:

@RunAs("my-special-user")

Of course, we then need to specify the real user that will run with that role, inside the weblogic-ejb-jar.xml custom DD, as follows:

<run-as-role-assignment>
   <role-name>my-special-user</role-name>
   <run-as-principal-name>weblogic</run-as-principal-name>
</run-as-role-assignment>

This enables the trust between containers; the EJB container will now trust that weblogic—authenticated on the serlvet container—has the my-special-user role and as such can also invoke its methods.

Securing the EJB module

We have just finished exploring how we can secure servlets and EJBs in the WAR module. The coding and configuration required is not different if we decide to deploy a Bean into an EJB Module; the only difference is that the weblogic-ejb-jar.xml file must be written into the src/main/resources/META-INF folder, and that the WAR and EJB Modules inside an EAR have different class loaders.

Of course, if your EAR has many WARs that share business logic, this is the desired deployment configuration; but if your application is composed of a single WAR and a single EJB, consider switching to a WAR-only deployment.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.6.243