Wednesday, July 30, 2008

Intro to JBoss Seam Security, Part 3 -- Authorization Rules with Drools

In previous articles, we have looked at how JBoss Seam handles security, particularly user authentication. Through the use of its Identity and Authenticator APIs, Seam provides an easy way to authenticate a prospective user without forcing a developer to implement hundreds of lines of code. In addition, Seam also provides CAPTCHA support, enabling an application to test for the presence of a human user, rather than a bot attempting to access an application. No doubt, Seam 2.1's forthcoming identity management API will make the process of creating and managing user accounts even easier. (Although, that is an article for another day)

While authentication is vital to application security, it is not the only aspect of security that must be accounted for. Authorization -- the process whereby it is determined what resources or actions a user may access -- takes authentication one step further, and cannot be overlooked. It is easy to come up with examples of where authorization should be determined within an application. A banking site would want to ensure that an authenticated user is, in fact, the owner of the bank account they are trying to access. A health insurance provider needs to check if an individual should be allowed to view recent claims that have been submitted. To accomplish this, some sort of authorization rules need to be established. Java EE already allows for programmtic authorization checks through the use of the HttpServletRequest.isUserInRole() method. Seam, however, takes authorization further and allows for integration within enterprise applications through the use of an authorization rules engine.

Seam uses the Drools business rules engine to allow complex authorization rules to exist outside of the Java code itself. Business rules engines have long been used to drive business operations or make decisions related to a specific business process (i.e. Approve a loan for an individual if they make more than $150,000 per year and have less than $25,000 in existing debt.) Now, these same types of rules can be applied to application security logic. Imagine a site like Facebook and how it approaches security. Facebook has very well-defined rules for how its information can be accessed. A profile is only visible to a member if the profile owner is friends with the member. Picture X can only be viewed by family members and friends, not coworkers. Member A can send a friend request to Member B provided that: 1) Member A != Member B, and 2) Member A is not already friends with Member B. It would be very easy to implement rules like this in Java code, either in the view or business layer. However, in our example application, we will separate these rules from our Java code and implement them using Drools.

Our simple example will be a social network site. Like any social network, ours will have some basic rules established to ensure the security of our site. The rules we will implement are:

  1. Administrators may do whatever they please
  2. A friend request may only be submitted if the sender != recipient
  3. A profile is only visible by its owner and their friends

Our example will consist of two main objects:


public class Member implements Serializable {

...snip...

public Set<Member>getFriends() {
...
}

public void setFriends(Set<Member> friends) {
...
}
}

@Name(value="member")
public class MemberAction {

...snip...

@In
private Member authenticatedMember = null;

public void viewProfile(Member member) {
...
}

public void sendFriendRequest(Member recipient) {
...
}

public void deleteProfile(Member member) {
...
}
}



For the sake of time, we will not discuss basic dependency injection concepts. It should be obvious that MemberAction.authenticatedMember is related to the profile of a user who has successfully authenticated against the application. There are two primary ways we can perform an authorization check in the business layer using Seam: 1) Through annotations, or 2) Inline, using Java code. In addition, we can also perform security checks in the view layer to display or hide links or data based on the outcome of the check. We will discuss all of these methods for performing the checks as we move forward.

Before we are able to write and test our security rules, we must first enable them in our Seam configuration, and ensure our rules file is located in the right place. The components.xml file must have the following lines to tell Seam that we wish to use Drools for authorization:


<drools:rule-base name="securityRules">
<drools:rule-files>
<value>/META-INF/security.drl</value>
</drools:rule-files>
</drools:rule-base>


As you can see, we have configured our security rules file to be located at /META-INF/security.drl. It is important that this file exists, otherwise our applications will not work as we expect. To tackle our first rule, "Administrators may do whatever they please", we need to establish a rule that allows administrators to be granted whatever permissions they request. Since this is a relatively simple rule, it will serve as a good starting point for our introduction into Drools syntax. Our rule will look like this:



package MySecurityRules;

import org.jboss.seam.security.PermissionCheck;
import org.jboss.seam.security.Role;

rule AdminsCanDoAnything
when
c: PermissionCheck(name == "member")
Role(name == "admin")
then
c.grant();
end;


Let's take a look at the syntax for this rule. On line 1, you will see a package declaration. This is not the same as Java packages. Drools packages are used to group Drools rules together. They are not used for anything else. On lines 2 and 3, you see two import statements. This lets Drools know that we will be referrencing the specified classes as part of our rule. It is imporant to include these lines, otherwise our rule will fail. Line 4 marks the beginning of the rule itself. Our rule is called "AdminsCanDoAnything". There is no specific naming requirement for your rule, other than the fact that it must be unique within the given package. Typically, the rule name is a description of what the rule actually accomplishes. The rule is divided into 2 sections, the "when" and the "then". The "when" section consists of one or more conditions that must be true for this rule to fire. If any of these conditions is not met, the rule will not fire. Once all conditions have been met, the rules engine moves on to the "then" section, and performs whatever commands are specified.

Lines 6 and 7 provide the authorization check itself. Line 6 is the first condition that must be met for this rule to fire. The line:


c: PermissionCheck(name == "member")


can be explained as, "there must exist a PermissionCheck object with a name property of "member" within the current working memory. This object will be called 'c' in this rule." If this condition is met, the engine moves on to line 7. Line 7 specifies that, "there must exist a Role with the name 'admin' within the current memory." By now you're probably wondering, "What is 'the current memory'?" Drools has its own context or working memory that it uses when evaluating a rule. It essentially acts as a session for the rules engine. Before an object can be considered within a rule, it must be added to the working memory. Whenever a permission check is performed via the hasPermission() method, a PermissionCheck object is created and inserted into the working memory. This object contains the name of the object the check corresponds to, and the action that is being attempted. In our example, the name of the object is "member". You'll notice that we have not specified an action in our check. This results in the rule corresponding to all possible actions pertaining to member. In addition, any Roles that a user belongs to will be inserted into the working memory prior to a rule check. This is how we determine the contents of our example security rule. It is also possible to insert an arbitrary object into the working memory by calling


((RuleBasedIdentity) RuleBasedIdentity.instance()).getSecurityContext().insert();


This will come in handy in our next article as we discuss Seam security rules futher. Now that we have defined our authorization rule, we can apply it and perform a permission check. For this example, we want to hide the link for the deleteProfile action from our view layer. Normally, however, we would also apply this permission check in the action itself to prevent users from circumventing our view-layer check. To perform a permission check on our link, we simply add the following to our JSF file:


<s:link value="Delete Profile" action="#{member.deleteProfile(...)}"
rendered="#{s:hasPermission('member', 'deleteProfile', null)}" />


Now, when our view is displayed, the permission check will be performed prior to rendering the link. Since our admin users have been granted all permissions, the link will be rendered. If we wrote an additional rule restricting access to the deleteProfile(), our admins would still be able to view the link, while other users that fail the permission check will not see the link.

In my next article, I will discuss how to implement our two remaining authorization rules, as well as the other ways of performing a permission check within your code.

Saturday, July 26, 2008

Building Seam Apps for Tomcat with JBoss Tools

One of the things I really like about the Seam framework is how much easier J2EE development is, particularly with respect to pulling the various components together. With Seam, it's very easy to build applications that utilize JSF, EJBs, and JPA. However, it's often preferable to develop more lightweight applications. While EJB3 has made enterprise development much more lightweight than in the past, there is still considerable overhead associated with it, particularly when you make use of a full-blown J2EE application server like JBoss. Sometimes, it's better to utilize a simpler approach and deploy your application using a Servlet Container like Tomcat.

The JBoss Tools project goes a long way towards making the actual development process with Seam much more user friendly. JBoss Tools provides a set of Eclipse plugins designed to work seamlessly (pun intended) with the framework. One of the drawbacks of JBoss Tools, however, is its lack of proper integration when creating a project you wish to deploy to Tomcat. In this post, I'll show you the changes you'll need to make to your Eclipse projects if you want to deploy your application to Tomcat.

To start, I'll assume that you have already downloaded Tomcat and defined a Server for it in Eclipse. It's very important to remember that you need Tomcat 6, in order to have support for the Servlet 2.5 specification. Once you've downloaded and setup Tomcat in Eclipse, you're ready to create your JBoss Tools project.

Create your JBoss Tools Seam project, just as you would a regular Seam project, by clicking File->New->Seam Web Project. This will start the new project wizard. However, when selecting your Target Runtime, select your Apache Tomcat 6.0 runtime, as shown in the image below.



Click next, and select whatever project facets you may need. Click next through the wizard and fill in the appropriate fields according to your project needs, until you get to the following screen.



In this screen, be sure to select a WAR file for your deployment, as Tomcat only understands WARs, not EARs. Click Finish, and your project will be created. Now, we need to make a couple of changes to support Tomcat in our project. We'll start by ensuring that the necessary Seam libraries are a part of your project. The following libraries need to be placed in your WEB-INF/lib directory:

  • antlr.jar
  • asm.jar
  • cglib.jar
  • commons-beanutils.jar
  • common-collections.jar
  • commons-digester.jar
  • commons-lang.jar
  • commons-logging.jar
  • dom4j.jar
  • hibernate-annotations.jar
  • hibernate-commons-annotations.jar
  • hibernate-entitymanager.jar
  • hibernate.jar
  • hibernate-validator.jar
  • javassist.jar
  • jboss-archive-browsing.jar
  • jboss-el.jar
  • jboss-seam-debug.jar
  • jboss-seam.jar
  • jboss-seam-ui.jar
  • jsf-api.jar
  • jsf-facelets.jar
  • jsf-impl.jar
  • jstl.jar
  • jta.jar
  • persistence-api.jar
  • richfaces-api.jar
  • richfaces-impl.jar
  • richfaces-ui.jar
Once these libraries are in place, your project will build successfully and deploy correctly to Tomcat. Now, we need to look at the Resource configuration to support JPA for object persistence. First, we need to create a context.xml file and place it your META-INF directory. This is the file that Tomcat uses when deploying your project to setup the context root, as well as any resources that need to be made available to your project. With a JBoss deployment, your database settings are placed in a separate file, like MyProject-ds.xml. With Tomcat, we will place all of these settings in our context.xml file. Once completed, it will look like this:


<?xml version="1.0" encoding="UTF-8"?>
<Context crossContext="true" debug="5" docBase="myproject" path="/myproject" reloadable="true">
<Resource auth="Container" driverClassName="com.mysql.jdbc.Driver" maxActive="20"
maxIdle="10" maxWait="-1" name="jdbc/myproject type="javax.sql.DataSource"
url="jdbc:mysql://localhost:3306/myproject"
username="myuser" password="mypassword" />
</Context>


We also need to modify the persistence.xml file used by JPA. The JNDI name that Tomcat utilizes differs from what JBoss uses. Instead of "java:/", our datasource needs to start with "java:comp/env". Our persistence.xml file will now look like this:


<persistence xmlns="http://java.sun.com/xml/ns/persistence" xsi="http://www.w3.org/2001/XMLSchema-instance" schemalocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="MyProject" type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:comp/env/jdbc/MyProjectDatasource</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup" />
</properties>
</persistence-unit>
</persistence>


Lastly, we need to modify some settings in our components.xml file. First, remove the jndi-pattern="@jndiPattern@" attribute from the following line:


<core:init debug="true" pattern="@jndiPattern@">


At this point, we should be ready to deploy our applicaion and test it out. Take a good look at the log files in case your application doesn't deploy correctly. However, if you followed the above steps, everything should work as expected.