Access Keys:
Skip to content (Access Key - 0)
 
Toggle Sidebar

Secure JSF components

Added by Tommy Holm, last edited by Matt Raible on Apr 09, 2007 22:25


The Challenge

Very often when building website with lots of different pages you end up with some pages that are accessible to users with certain roles. In a simple case you could have a normal user role and an admin role much like the default AppFuse setup. So you would have pages a normal user role could only access and also pages that an admin role could only access. Now even in this simple case, you would most often have pages that are accessible to both the admin role and the normal role.

Although the page is accessible to both roles you might have information on that same page that only is should be visible to a certain role. Another common scenario is that you have a list of users but the button to edit a user should only be visible to the admin role.The most common technique to my knowledge is to wrap the information or button, in some kind of of tag like:

 <..:isUserInRole role="admin"> BUTTON ONLY VISIBLE TO ADMIN ROLE</...:isUserInRole>

In AppFuse you would probably use the Acegi tags for doing this stuff. I think this is a very bad practice; you end up with very messy pages which are hard to maintain. Let's say you had a very small site with 50 pages. If you add a role you would have to go through all your pages and edit the isUserInRole tags. Bad if you ask me! This example was only a simple; imagine you had 20 different roles, like economy role, admin role, IT role etc.

The Proposed Solution

First of all this idea is inspired by an article written by Rick Hightower and his work inspired me to write a Maven-based solution. Be aware that this solution is based on the MyFaces implementation of the JSF specification and therefore of course is written for Appfuse with the JSF front-end.

As we are using a JSF front-end you probably would know that the pages contain JSF components. A JSF component could be the following.

<h:commandButton value="#{text['button.delete']}" action="#{userForm.delete}" 
    styleClass="button" onclick="bCancel=false; return confirmDelete('User')"/>

Now to avoid wrapping the component in some userole custom tag or a JSTL if statement would it not be nice if the commandButton would know by itself to be rendered or not? I think so and it would be a very clean approach for the UI designer as he/she would not need to think about enabling or disabling this button. (Note: Tomahawk component are security aware and you can write the roles as attributes to the component).

So what we want to achieve is that all myfaces jsf components will know if to render themselves or not. Sounds kind of tricky because we don't want to extend all myfaces components and write custom code but the solution is pretty simple. We will use an aspect!

All JSF components do implement methods called encodeBegin, encodeEnd and encodeChildren. These methods are responsible for rendering the component. Sometimes the method itself contains the rendering code but most often the method delegates the rendering to a dedicated renderer! What we need to do is to intercept these method calls and to call our own code which should then be responsible for deciding if the component should be rendered! Intercepting these method call and inserting our own security code in the already compiled jars, is a job for an aspect and we will use the popular aspectj compiler for this.

How

The steps involved for accomplishing our objective are:

  1. Add the aspectj compiler dependency to your pom file
  2. Add the jar files to be weaved
  3. Write your aspect
  4. Write your security code

Step1

We need to add the aspectj compiler to our pom file. Some projects can consist of more than one pom.xml file, so you have to decide where you want to put the dependency. Adding it to the outermost pom.xml file should be fine! Add the dependency:

<dependency>
    <groupId>aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.5.0</version>
</dependency>

Step2

Now we need to add the jar files to be weaved with our custom code. In our case it is the myfaces.jar files but if you would use other third party JSF components, you would do it the same way. The jar files need to be added to a pom file by adding a aspectj plugin configuration. Now the JSF components are used in the webapp so I suggest you add the needed configuration in the pom.xml file of your webapp. Do the following:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <configuration>
        <weaveDependencies>
            <weaveDependency>
                <groupId>org.apache.myfaces.core</groupId>
                <artifactId>myfaces-api</artifactId>
            </weaveDependency>
            <weaveDependency>
                <groupId>org.apache.myfaces.core</groupId>
                <artifactId>myfaces-impl</artifactId>
            </weaveDependency>
        </weaveDependencies>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
   </executions>
</plugin>

Now in the lines above we first declare the plugin and then we configure the plugin.  What happens is that we configured the plugin to aspectj-compile the two libraries <groupId>org.apache.myfaces.core</groupId> <artifactId>myfaces-api</artifactId> and <groupId>org.apache.myfaces.core</groupId> <artifactId>myfaces-impl</artifactId> with any aspect found in your source code. Your custom code will be weaved into the jars. Remember for this to work you must have the two libraries defined as dependencies somewhere your poms, I have them just below the plugin configuration som my configuation looks like this:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
        </plugin>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>dbunit-maven-plugin</artifactId>
        </plugin>
        
        <plugin>
            <groupId>org.appfuse</groupId>
            <artifactId>maven-warpath-plugin</artifactId>
        </plugin>       
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <configuration>
                <weaveDependencies>
                    <weaveDependency>
                        <groupId>org.apache.myfaces.core</groupId>
                        <artifactId>myfaces-api</artifactId>
                    </weaveDependency>
                    <weaveDependency>
                        <groupId>org.apache.myfaces.core</groupId>
                        <artifactId>myfaces-impl</artifactId>
                    </weaveDependency>
                </weaveDependencies>
            </configuration>
        <executions>
            <execution>
                <goals>
                    <goal>compile</goal>
                </goals>
            </execution>
        </executions>
        </plugin>
    </plugins>
</build>

<dependencies>
    <dependency>
        <groupId>org.apache.myfaces.core</groupId>
        <artifactId>myfaces-api</artifactId>
        <version>1.1.4</version>
    </dependency>
    <dependency>
        <groupId>org.apache.myfaces.core</groupId>
        <artifactId>myfaces-impl</artifactId>
        <version>1.1.4</version>
    </dependency>
</dependencies>

Now lets get to the actual aspect!

Step3

In src->main folder create a folder called aspect. So now you have src->main-->aspect! In the aspect folder create a file called JSFComponentSecurityAspect.aj. Notice the aj extention. Paste the following information into the file!

import org.your.webapp.security.JSFComponentSecurityChecker;
import org.your.webapp.security.impl.SomeSecurityChecker;

import javax.faces.component.UIComponent;

public aspect JSFComponentSecurityAspect {
    private JSFComponentSecurityChecker securityChecker = null;

    public void setSecurityChecker(JSFComponentSecurityChecker checker) {
    	securityChecker = checker;
    }

    /*
        A point cut to capture all executions of encode methods (encodeBegin, encodeEnd, encodeChildren) of any UIComponent.
        hideComponents is the name of a pointcut. A pointcut is the part of the code that we are going to advise.
        execution means join on method execution
        void UIComponent+.encode*(..)) means any encode method returning void for any class that implements UIComponent
        this(component) is a capturing pointcut so we can access the current component
    */
    public pointcut secureComponents(UIComponent component) :
        execution (void UIComponent+.encode*(..)) && this(component);


    void around(UIComponent component) : secureComponents(component ) {
        //Should be injected by spring - This is a no go :)
        securityChecker = new SomeSecurityChecker();
        
        /* If this component is not secured, then allow the encode method of the component to execute. */
        if (!securityChecker.isSecured(component)){
            proceed(component);
            return;
        }
        
        /* Check to see if we are allowed to display the component.
         * If we are allowed, then proceed as usual.
         * If not, then don't proceed and setRendered to false.
         */
        if (securityChecker.isAllowed(component)){
            proceed(component);
        } else {
            component.setRendered(false);
        }
    }
}

Here we have the  aspect which first has a pointcut defenition. It will intercept all calls to the encode* methods and then call the around advice. An around advice means that the code in the advice gets inserted in the actual method call and therefore we have access to the actual UIComponent being called(see the this(component) which will add the current component to the around advice also called context passing). In the around advice  we first instantiate the security interface which of course is bad practice, you should let spring do that. Then we ask the security interface implementation if the current component is secured, if not then we call a special aspect method called proceed which means the component will continue to execute normally. If the component is secured we check if the current component is allowed for the current user(in my case it's a user but it could be anything depending on your implementation), if the user is allowed to access this component, proceed is called and the component method continues, otherwise we set the rendered attribute of the component to false which means it will not be rendered.

This is pretty much it, you of course need to define your interface and your implementation of the security interface but this is up to you, every company has it's own requirement. In my case I have a table in the database where all the component id's are registered with  their id's and then the mathing roles. Very much the same way the userrole stuff works! Then I made some administration GUI to manage it.

Here is my interface and some bogus implementation!

public interface JSFComponentSecurityChecker {

    /**
     * Checks to see if a component can be executed based on the implementations rules
     *
     * @param component component
     * @return allowed.
     */
    boolean isAllowed(UIComponent component);    

    /**
     * Check to see if the component is secured.
     *
     * @param component component
     * @return secured.
     */
    boolean isSecured(UIComponent component);   
}

import org.your.webapp.security.JSFComponentSecurityChecker;
import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.userdetails.UserDetails;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;

public class SomeSecurityChecker implements JSFComponentSecurityChecker {
    protected final Log log = LogFactory.getLog(getClass());
    
    public boolean isAllowed(UIComponent component) {
        if (log.isDebugEnabled()) {
            log.debug("Checking Component with id: "+component.getId() );
        }
        //here you should call business code to figure out if the component should be visible/rendered for the current user
        //possibly by checking some component/role/user type of table
        //In this dummy implementation it doesn't check properly, it simply returns true to indicate
        //that the component should be rendered
        
        return true;
    }
    
    public boolean isSecured(UIComponent component) {
        if (log.isDebugEnabled()) {
            log.debug("Checking if Component with id: "+component.getId() + "IS secure " + true);//TODO REMOVE
        }
        //All components are secure but you should have business code that figures out if component is secure
        return true;
    }
}

Bonus

I made a second aspect that can document the component, that mean because I have a very complex security setup I thought it would be nice that it would be possible to output comments to actual response so that when you do a view source on the resulting HTML page you can see comments if a component wasn't rendered because the current user doesn't have the correct permissions. This is of course only enabled if we are in development so that it will not be visible when running in production! This is only a prototype but I put it here for inspiration. The most difficult part was passing all the different parameters around and it took me some time to figure the aspect syntax out

import javax.faces.context.FacesContext;
import javax.faces.component.UIComponent;
import javax.faces.render.Renderer;
import javax.faces.context.ResponseWriter;
import java.io.IOException;

public aspect JSFComponentDocumentator {

   /**
    *  Intercept calls to any render component and provide the arguments to the around advice
    */
    public pointcut documentComponent(Renderer renderer, FacesContext context, UIComponent component) :
			execution (void Renderer+.encode*(FacesContext, UIComponent)) && target(renderer)&& args(context, component);

    void around(Renderer renderer, FacesContext context, UIComponent component) : documentComponent(renderer, context, component ) {
        ResponseWriter writer = context.getResponseWriter();
        try {
            //Here you could write information about a component which is not rendered as source comments.
            //This could be helpful in development where there could be comments in the html output generated 
            //by the renderer. So in a HTML page you could have a comment like: <!-- Component with ID someID is not 
            //rendered because user had role (someroles) and the required role for this component are (req roles)-->
            //Just an example what you could do
            writer.writeComment("Currently Rendering component with ID: " +component.getId());
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        //Proceed with the render method
        proceed(renderer,context,component);
    }
}

Adaptavist Theme Builder (3.1.4) Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.5.7 Build:#813 Aug 28, 2007)
Free theme builder license