TurboManage

David Chandler's Journal of Java Web and Mobile Development

  • David M. Chandler


    Web app developer since 1994 and former Developer Advocate with Google now residing in Colorado. Besides tech, I enjoy landscape photography and share my work at ColoradoPhoto.gallery.

  • Subscribe

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 231 other followers

  • Sleepless Nights…

    January 2019
    S M T W T F S
    « Nov    
     12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
  • Blog Stats

    • 971,239 hits

Archive for the ‘GIN / Guice’ Category

Calling AppEngine (securely) from GWT with gwt-dispatch

Posted by David Chandler on October 7, 2009

When using an AJAX framework like GWT, it is necessary to authenticate each service call as well as the front end or else your services are open to the world. With AppEngine, there are several approaches we could take, such as wrapping all service servlets with a servlet filter or Spring Security or calling a checkLoggedIn() method as the first statement in each service method.

The mechanics of authenticating an AppEngine on the server side are simple enough. We just call the AppEngine UserService. The browser automatically passes the AppEngine cookie with each service request, so we don’t have to modify our service interfaces just to authenticate the user. This example shows a simple checkLoggedIn() method that could be called from each service method or wired into a servlet filter:

package com.turbomanage.server.util;

import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.turbomanage.exception.NotLoggedInException;

public class ServiceUtil
{
	public static User checkLoggedIn() throws NotLoggedInException
	{
		User user = getUser();
		if (user == null)
		{
			throw new NotLoggedInException("Not logged in.");
		}
		return user;
	}

	private static User getUser()
	{
		UserService userService = UserServiceFactory.getUserService();
		return userService.getCurrentUser();
	}
}

In addition to authenticating the user, it’s a good idea to protect our services against CSRF/XSRF attacks. Google recommends (see section on XSRF and GWT) that each service request be accompanied by a token that is available to the server and the client application, but not to JavaScript code in an email link or running on otherĀ  pages. The simplest scheme is simply to pass the sessionId cookie as an argument to each service method. With standard GWT-RPC, however, this is a pain because we have to add an argument to every method of every service:

public interface MyService extends RemoteService {
  public boolean doSomething(String cookieValue);
  public void doAnotherThing(String cookieValue, String arg);
}

This is where gwt-dispatch comes to the rescue and makes you thankful for people who turn verbs into nouns. Because gwt-dispatch uses the Command pattern, all standard GWT-RPC services are replaced with a single DispatchService that accepts an Action and Result argument. Each method in a standard GWT-RPC service is now represented by a corresponding Action and Result class. Because there is now only one real GWT-RPC service, the DispatchService, and because its execute method gets called for every request, we now have only place we need to add a session token argument. The gwt-dispatcher SecureDispatchService does exactly this:

package net.customware.gwt.dispatch.client.secure;

import net.customware.gwt.dispatch.shared.Action;
import net.customware.gwt.dispatch.shared.Result;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("dispatch")
public interface SecureDispatchService extends RemoteService {
    Result execute( String sessionId, Action<?> action ) throws Exception;
}

By implementing this service on the server, we now have one place where we can verify the user is logged in as well as verify that the session ID passed as an argument with the request matches the session ID cookie sent automatically by the browser.

So here is my take on an AppEngineDispatchService that builds on gwt-presenter’s SecureDispatchService to achieve both user authentication and a measure of CSRF/XSRF protection. Because gwt-presenter’s SecureDispatchAsync class has a dependency on its own SessionUtil class, I had to replace it with AppEngineDispatchAsync; however, this could easily be factored out as an interface in a future release of gwt-presenter (and indeed has been already on the server side).

One last thing to mention: AppEngine uses a cookie named “ACSID”, so that’s what we’re passing with each request.

Our first class is just a stripped-down version of gwt-presenter’s SecureDispatchServiceAsync which passes the AppEngine session ID as an argument with each request.

package com.turbomanage.gwt.client.dispatch;

import net.customware.gwt.dispatch.client.DispatchAsync;
import net.customware.gwt.dispatch.shared.Action;
import net.customware.gwt.dispatch.shared.Result;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Cookies;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.turbomanage.gwt.client.service.AppEngineDispatchService;
import com.turbomanage.gwt.client.service.AppEngineDispatchServiceAsync;

/**
 * An AppEngine-aware implementation of {@link DispatchAsync}
 *
 * (c) 2009 David M. Chandler
 * Licensed under the Apache License, Version 2.0
 */
public class AppEngineDispatchAsync implements DispatchAsync {

    private static final AppEngineDispatchServiceAsync realService = GWT.create( AppEngineDispatchService.class );

    public AppEngineDispatchAsync() {
    }

    /* (non-Javadoc)
	 * @see com.turbomanage.gwt.client.dispatch.AppEngineSecureDispatchServiceAsync#execute(A, com.google.gwt.user.client.rpc.AsyncCallback)
	 */
    public <A extends Action<R>, R extends Result> void execute( final A action, final AsyncCallback<R> callback ) {

    	// Get AppEngine session ID
        String sessionId = Cookies.getCookie("ACSID");

        realService.execute( sessionId, action, new AsyncCallback<Result>() {
            public void onFailure( Throwable caught ) {
                callback.onFailure( caught );
            }

            public void onSuccess( Result result ) {
                callback.onSuccess( ( R ) result );
            }
        } );
    }

}

To use this class instead of gwt-presenter’s Standard or SecureDispatchAsync, simply modify the binding in your GIN module:

    @Override
    protected void configure() {
    ...
        bind( DispatchAsync.class ).to( AppEngineDispatchAsync.class ).in( Singleton.class );
    ...
    }

Now for the service interfaces. These are just renamed from gwt-presenter’s SecureDispatchService and SecureDispatchServiceAsync, which was necessary because I could not use gwt-presenter’s SecureDispatchServiceAsync on account of the above-mentioned dependency on SessionUtil and GWT seems to require that a service and its corresponding Async class exist in the same package.

package com.turbomanage.gwt.client.service;

import net.customware.gwt.dispatch.shared.Action;
import net.customware.gwt.dispatch.shared.Result;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("dispatch")
public interface AppEngineDispatchService extends RemoteService {
    Result execute( String sessionId, Action<?> action ) throws Exception;
}

And the corresponding Async interface, likewise just renamed from gwt-presenter’s SecureDispatchServiceAsync:

package com.turbomanage.gwt.client.service;

import net.customware.gwt.dispatch.shared.Action;
import net.customware.gwt.dispatch.shared.Result;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface AppEngineDispatchServiceAsync
{
	void execute(String sessionId, Action<?> action,
			AsyncCallback<Result> callback);
}

Finally, our server implementation of the DispatchService:

package com.turbomanage.gwt.server.service;

import java.util.logging.Logger;

import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import net.customware.gwt.dispatch.client.secure.SecureDispatchService;
import net.customware.gwt.dispatch.server.Dispatch;
import net.customware.gwt.dispatch.shared.Action;
import net.customware.gwt.dispatch.shared.Result;
import net.customware.gwt.dispatch.shared.secure.InvalidSessionException;

import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.turbomanage.gwt.client.service.AppEngineDispatchService;

/**
 * A servlet implementation of the {@link SecureDispatchService}. This verifies
 * that an AppEngine user is logged in and inspects the passed AppEngine
 * session ID to prevent CSRF/XSRF attacks
 *
 * (c) 2009 David M. Chandler
 * Licensed under the Apache License, Version 2.0
 */
@Singleton
public class AppEngineDispatchServiceServlet extends RemoteServiceServlet
		implements AppEngineDispatchService
{
	private static final long serialVersionUID = -1456388230348266500L;
	private static final Logger LOG = Logger.getLogger(AppEngineDispatchServiceServlet.class
			.getName());

	private final Dispatch dispatch;
	private Provider<HttpServletRequest> req;

	@Inject
	public AppEngineDispatchServiceServlet(Dispatch dispatch, Provider<HttpServletRequest> req, Provider<ServletContext> context)
	{
		this.dispatch = dispatch;
		this.req = req;
	}

	public Result execute(String clientSessionId, Action<?> action) throws Exception
	{
		try
		{
			String serverName = req.get().getServerName();
			UserService userService = UserServiceFactory.getUserService();
			User user = userService.getCurrentUser();
			if (user != null)
			{
				// User is logged in, now try to match session tokens
				// to prevent CSRF
				String sessionId = "";
				Cookie[] cookies = req.get().getCookies();
				for (Cookie cookie : cookies)
				{
					if (cookie.getName().equals("ACSID"))
					{
						sessionId = cookie.getValue();
						break;
					}
				}
				// Skip check on localhost so we can test in AppEngine local dev env
				if (("localhost".equals(serverName)) || (sessionId.equals(clientSessionId)))
				{
					return dispatch.execute(action);
				}
			}
			throw new InvalidSessionException();
		}
		catch (RuntimeException e)
		{
			LOG.warning("Exception while executing " + action.getClass().getName()
					+ ": " + e.getMessage());
			throw e;
		}
	}
}

For each request, we’re simply calling the AppEngine UserService to verify the user is logged in and attempting to match the AppEngine cookie passed as an argument with the one passed as a cookie. In order to get access to the cookie on the server, we utilize a Guide Provider that lets us get to the ServletRequest. Likewise, we obtain the ServletContext in order to get the server name and skip the CSRF check on localhost, since AppEngine doesn’t set the ACSID cookie when running in the local development environment.

Lastly, we replace the gwt-presenter Standard or SecureDispatchServiceServlet with our AppEngine equivalent in the server’s Guice module:

	@Override
	public void configureServlets()
	{
		// NOTE: change the servlet context for your app
		serve("/your_app/dispatch").with(AppEngineDispatchServiceServlet.class);
	}

Thanks to gwt-presenter and the Command pattern, every AppEngine service request in our application now checks for a logged in user and provides a measure of CSRF protection.

Advertisements

Posted in AppEngine, GIN / Guice, Google Web Toolkit, Model-View-Presenter, Web App Security | 23 Comments »

GWT + MVP = Very Manageable Code

Posted by David Chandler on September 24, 2009

I’ve been struggling with GWT a bit. It’s a new way of thinking for someone who’s been writing Web apps on the server side for 15 years now. But I was intrigued by a few posts on GWT with MVP, and then I found this:

http://blog.hivedevelopment.co.uk/2009/08/google-web-toolkit-gwt-mvp-example.html

I cannot say it or do it any better. It took me about a day to rewire my small GWT + AppEngine app with gwt-presenter and gwt-dispatch. It worked the first time. Color me impressed.

I can understand my own GWT code far more easily now.

And it’s more testable.

And more modular so other developers can work on it at the same time.

Did I mention I was impressed?

For the CM-inclined, these are the jar versions I put in my GWT project’s lib directory:

  • gwt-dispatch-1.0.0.jar from the project’s Downloads page
  • gwt-presenter-1.0.0.jar from the project’s Downloads page
  • gin.jar from GIN project’s out/dist folder I built from SVN trunk on 9/23/09
  • guice-2.0.jar from GIN’s lib folder per the blog post above

Posted in GIN / Guice, Google Web Toolkit, Model-View-Presenter | 4 Comments »

Getting Started with GIN and Guice

Posted by David Chandler on September 23, 2009

As part of the RememberOneAnother project, I’m using the excellent gwt-presenter and gwt-dispatch frameworks, which in turn depend on GIN and Guice. You can download the Guice jars from the project site, but you have to build GIN. I did the following in Eclipse:

  1. Set a GWT_HOME env variable to C:\eclipse\plugins\com.google.gwt.eclipse.sdkbundle.win32_1.7.0.v200907291526\gwt-windows-1.7.0 and restart Eclipse to pick up the new var.
  2. Download Guice.
  3. Check out GIN from SVN (see the project Source tab for location) as a new Java project using the New Project Wizard.
  4. Add the guice-2.0.jar to the GIN project’s build path.
  5. Right-click on the GIN build.xml and click Run As Ant Build… In the Targets tab, select the “dist” target. In the JRE tab, make sure a JDK is specified and not just a JRE.
  6. Click Run to build GIN. The resulting gin.jar will be put in the out/dist folder. You may need to unfilter .* resources to see it.

Posted in GIN / Guice, Google Web Toolkit | Leave a Comment »

 
%d bloggers like this: