A core principle of a good application security architecture is to maintain an appropriate Separation of Concerns. We need to avoid tangling the code for the application’s business logic, with the code that implements the security enforcements. In general, the best way to accomplish this is to leverage framework-based enforcement mechanisms whenever possible. My previous post provided a working example of how to do this in practice, using EJB 3 container-based security with Apache Geronimo. We showed how to enforce Role-based Access Control for both Web resources and EJB methods, using the standard XML deployment descriptors. We implemented all of the required security enforcements without making any changes to the actual application code.
Unfortunately, the container-based security enforcement provided by a standard JEE deployment descriptor is limited to protecting object types. That is, we can easily enforce a policy that says “only tellers may view customer accounts.” But what if we have a requirement to enforce permissions on specific object instances? In that case, the declarative approach alone would be insufficient (for one thing, we don’t want to redeploy the application every time a new object instance is created!). In order to do any type of instance-based policy enforcement, we’ll need to use another mechanism.
Fortunately, EJB 3 also includes the capability to use Aspect Oriented Programming techniques in your application. A full discussion of AOP techniques is beyond the scope of this post, but the basic idea is that one can inject (at runtime) additional code that implements logic for cross-cutting concerns. For example, one could add additional logging into an existing set of classes. Of course, security is my favorite cross-cutting concern, and so in this post I’ll discuss how to use an EJB 3 Interceptor to provide this instance-based policy enforcement. Because the interceptor is code is completely decoupled from the application code it is again possible to maintain an appropriate Separation of Concerns. We can add the needed security enforcement logic without impacting the existing business logic. All we’ll need to do is implement the stand-alone interceptor class, and then wire it into the target application(s) via the deployment descriptor, and redeploy.
How To Do It
There are basically four steps to making this all work:
- Implement an interceptor class that will perform the needed security enforcements, and bundle it as a jar file.
- Update the application’s EJB deployment descriptor to declare the interceptor class, and bind it to a target EJB class and/or method.
- Repackage the enterprise application EAR file to include both the new jar files, and the updated deployment descriptor.
- Redeploy the enterprise application.
The following paragraphs provide some additional details on each of these steps. Note that the complete source code for this example is available over on my GitHub repository. You’ll want to clone both the psi-jee project (the Pivotal Security Interceptor for JEE), and also the mytime project (the target application).
Implementing the Interceptor Class
I implemented an interceptor that provides an around advice that will examine the values of the arguments passed to the target EJB when it is invoked. If the values of the calling arguments are consistent with the application security policy, then the call can be allowed to proceed. If the values of the arguments would violate a security policy, then the user’s request would be denied. As an illustrative example, consider a hypothetical EJB interface that performs a funds transfer function. The declarative security enforcements captured in the deployment descriptor could ensure that only users with the right role can access the funds transfer interface. But only the interceptor will be able to examine the actual values of the parameters passed into the funds transfer method at runtime — these arguments might be String types such as the “FromAccountNumber,” the “ToAccountNumber,” and the “TransferAmount.” (Of course this is an over-simplified example, but you get the idea).
The interceptor class has an interface contract with just one calling argument, the InvocationContext. This InvocationContext parameter can be used to obtain the actual arguments that were passed to the EJB that we are in the process of intercepting. This can be seen in lines 45-47 of the example code. Of course, the specific EJB arguments returned will vary from one application to the next, but with appropriate knowledge of the application bean(s) you are planning on intercepting, you should know what arguments you need to look for in the params array, and what test(s) to apply.
OK, so now we know what this EJB invocation is trying to do, but we still need to know who is trying to do it.
Since we are running in an EJB container, we can always get a handle to the current EJBContext. From there we can obtain the currently authenticated principal (the user id). Looking at lines 73-77 of the code, you can see that I defined a private variable to hold the EJB SessionContext, and then used the getCallerPrincipal() method to find out the userid of the current caller.
Thus, with just a few lines of code we can get access to who is calling the bean method, and what they are trying to do. Given that runtime context we can then implement whatever security policy checks are appropriate for the business. If all is well, we call the invocationContext.proceed() method. Otherwise, we can throw an exception.
Updating the Deployment Descriptors
The new interceptor class must be included in the target application’s deployment descriptor. This is makes perfect sense, since this is how the EJB container knows that you have defined an interceptor class, and also which EJBs are being targeted by the interceptor. I won’t repeat the essential snippet of XML here, as it is already captured in the README file of the mytime example that I’ve posted on GitHub. Of course, the complete deployment descriptor can also be found in the file META-INF/ejb-jar.xml, in the mytime repository.
Repackaging the Target Application
Before you re-package the target enterprise application, you’ll need to make at least one change to the maven POM of your ejb-jar project.
If your target application’s ejb-jar project does not already use the maven-jar-plugin, then you’ll want to add this plugin to the <build>/<plugins> element of the target EJB jar project’s maven POM. Using the maven-jar-plugin with the configuration as shown will ensure that the MANIFEST.MF file included within the EJB jar will contain a required Classpath: directive. Basically, you need to have a line in the MANIFEST.MF that tells the container to include the new jar (i.e. the interceptor jar) on the class path of the EJB at runtime. Take a look inside the EJB jar file after the re-build completes and you should see that the interceptor jar has now been added to that line that says Classpath: … psi-jee-1.0.0.jar).
If you’re already using the maven-jar-plugin in this way, then you’ll just need to add the dependency for the new interceptor jar, and the Classpath: value should be updated accordingly.
As noted, I’ve posted all the code for both the interceptor and the target application over at GitHub, so feel free to do a git clone and get started on using EJB 3 interceptors for instance-based enforcements. Pull requests are always welcome.
While this post has provided specific implementation guidance for an EJB 3 deployment, the real point here is not that we advocating the use of EJB. Rather, the real take-away is that a good security architect always follows the principle of Separation of Concerns. Using an interceptor is always good security technique, and fortunately EJB now has support for that. Security enforcement for object types are delegated to the container. Security enforcement for object instances are delegated to an interceptor. The application developer stays focused on the business logic. The result will be a system that is both more secure, and more maintainable.
If you’re not working in an EJB environment, then this same architectural pattern can still be applied to other containers. The approach would be similar for, say, instance-based policy enforcements for a RESTful service, accessed over HTTP(S) using a servlet filter deployed to Tomcat, or Jetty.
Finally, if you need to do the enforcements in a container-independent way (perhaps because you need to deploy your application to a number of different containers at runtime), then using an application developer framework such as the Spring Framework with a Spring Security filter chain is clearly the way to go.