Friday, December 24, 2010

GWT Simple Security Configuration

For my capstone project in my MSE program at Carroll University, I am writing a GWT application. One of the requirements for this application is security. There are several posts on securing a GWT application on the Web already, but most of them are more heavy weight solutions that show you how to use Spring-AOP and Spring-security to secure your app.

For this project, I don't need anything that heavy-weight. Here is a little review of how I secured this application.

First, I wanted each service to have it's own access controls. More specifically, I want to be able to secure each method in each service. Second, I want to be able to support Role-based access. This way I can add users to roles to grant them the permissions they need. Finally, I don't have a real need to redefine role access in real-time. Given these constraints, I was able to put together a quick and easy solution to secure my application.

First, I decided that I wanted to be able to easily see in my code what the access control was. It seemed to me that Annotations would be an easy way to do this. Something like:

@Secure("ROLE_ADMINISTRATOR")

But I want to be able to specify multiple roles that can be configured to this action, so, multiple roles would look more like this:

@Secure({"ROLE_ADMINISTRATOR, ROLE_CONFIGURATION_MANAGER"})

So, the first thing I did was define the Annotation.


package com.archie.sma.server.util.security;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Secure {
  public String[] value();
}
  
AS you can see, annotations are annotated. This one says that this annotation applies to Methods, and it is kept at runtime.

Next I needed to apply the annotation to my services. This was quite easy. In my Service interface on the client, I used the annotation:

@Secure({"ROLE_ADMINISTRATOR", "ROLE_CONFIGURATION_MANAGER"})
public Object get(String id) throws Exception {
//Method implementation here...
}

Of course, I now need to tie these two together. Since I'm Using GWT, I decided to Extend the GWTServerServlet. By extending the servlet and over-riding the processCall and onAfterRequestDeserialized method, I am able to easily use a little bit of Reflection to get the annotation and determine if the user in Session has access to the method. This approach also requires me to extend my new security Servlet for all my secured classes. So its on the developer to ensure he is extending the class correctly. Here is the class:





public class SecureRemoteServiceServlet extends RemoteServiceServlet {

  private transient IDaoFactory daoFactory;

  @Override

  public void init(ServletConfig configthrows ServletException {

    super.init(config);

    daoFactory = new DaoFactory();

  }

  /**

   

   */

  private static final long serialVersionUID = 1000L;

  private static final String USER = "USER";

  private User user;

  @Override

  protected final void onAfterRequestDeserialized(RPCRequest rpcRequest) {

    Method method = rpcRequest.getMethod();

    Secure secure = method.getAnnotation(Secure.class);

    Boolean hasAccess = Boolean.FALSE;

    if (secure != null) {

      

      String[] roles = secure.value();

      HttpServletRequest request = getThreadLocalRequest();

      user = (Userrequest.getSession().getAttribute(USER);

      if (user != null) {

        List<Role> userRoles = user.getRoles();

        for (Role role : userRoles) {

          for (int i = 0; i < roles.length; i++) {

            if (role.toString().equals(roles[i])) {

              if(hasAccess != null) {

                hasAccess = Boolean.TRUE;

              }

            }

          }

        }

      }

    }

    if (!hasAccess){

      throw new RuntimeException("Not Authorized");

    }

  }

  /*

   * @Override public final String processCall(String payload) throws

   * SerializationException { if (!hasAccess) { throw new

   * UnexpectedException("Security Exception", new Exception(

   * "Not Authorized")); } return super.processCall(payload); }

   */



  public User getUser() {

    return user;

  }



  public void setUser(User user) {

    this.user = user;

  }



  public IDaoFactory getDaoFactory() {

    return daoFactory;

  }



  public void setDaoFactory(IDaoFactory daoFactory) {

    this.daoFactory = daoFactory;

  }

}


So that is my quick and dirty security implementation for my GWT app. This is not what you would call an enterprise solution, but it is a quick way to implement security for a little, self-contained application.