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 242 other followers

  • Sleepless Nights…

    February 2010
    S M T W T F S
    « Jan   Mar »
     123456
    78910111213
    14151617181920
    21222324252627
    28  
  • Blog Stats

    • 849,257 hits

“Generic” Objectified ActionHandler for gwt-dispatch

Posted by David Chandler on February 22, 2010

WARNING: use of a generic ActionHandler for persistence as described in this post has significant security implications, especially if you’re not using the SecureDispatchService from gwt-dispatch. See comments for more details.

In response to a reader request, I’ve come up with “generic” gwt-dispatch Action/Result/ActionHandler classes using Objectify. It is not quite as satisfying as I would like because it doesn’t use parameterized types like the ObjectifyDao I recently posted; however, as noted in an earlier post, Objectify itself doesn’t require parameterized types and therefore a parameterized DAO really isn’t that useful (nor possible as explained at the conclusion of this posting).

So here’s a PutAction/PutResult/PutHandler that can persist any object in the Datastore. Thanks to Dan Billings for making a valiant effort at a parameterized version of these classes and prompting me to write this. Creating an Action/Result/Handler combo for each Objectify operation (delete, query, etc.) is left as an exercise to the reader…

PutResult.java

package com.turbomanage.gwt.shared;

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

import com.googlecode.objectify.Key;

public class PutResult implements Result {

       private Object obj;
       private Key key;

       protected PutResult()
       {
    	   // Needed for GWT-RPC
       }

       public PutResult(Object object, Key key) {
               this.obj= object;
               this.key = key;
       }

       public Key getKey() {
               return key;
       }

       public Object getObject() {
               return obj;
       }

}

PutAction.java

package com.turbomanage.gwt.shared;

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

public class PutAction implements Action<PutResult>
{
	private Object obj;

	public PutAction(Object object)
	{
		if (object == null)
			throw new IllegalArgumentException("Object cannot be null");
		this.obj = object;
	}

	protected PutAction()
	{
		// Needed for GWT-RPC
	}

	public Object getObject()
	{
		return obj;
	}

}

PutHandler.java

package com.turbomanage.gwt.server.handler;

import net.customware.gwt.dispatch.server.ActionHandler;
import net.customware.gwt.dispatch.server.ExecutionContext;
import net.customware.gwt.dispatch.shared.ActionException;

import com.googlecode.objectify.Key;
import com.googlecode.objectify.Objectify;
import com.turbomanage.gwt.shared.PutAction;
import com.turbomanage.gwt.shared.PutResult;

public class PutHandler implements
ActionHandler<PutAction, PutResult>{

	@Override
	public PutResult execute(PutAction action, ExecutionContext arg1) throws ActionException
	{
		Objectify ofy = ObjectifyService.begin();
		Object obj = action.getObject();
		Key<Object> key = ofy.put(obj);
		return new PutResult(obj, key);
	}

	@Override
	public Class<PutAction> getActionType()
	{
		return PutAction.class;
	}

	@Override
	public void rollback(PutAction arg0, PutResult arg1, ExecutionContext arg2)
		throws ActionException
	{
		// TODO Auto-generated method stub
	}

}

The handler must be registered in a Guice module as usual:

package com.roa.server.guice;

import net.customware.gwt.dispatch.server.guice.ActionHandlerModule;
import com.roa.common.domain.User;
import com.turbomanage.gwt.server.handler.PutHandler;
import com.turbomanage.gwt.shared.PutAction;

public class ServerModule extends ActionHandlerModule
{
	static {
		ObjectifyService.register(User.class);
	}

	@Override
	protected void configureHandlers()
	{
		...
		bindHandler(PutAction.class, PutHandler.class);
		...
	}
}

Note that I’ve sneaked (snuck?) the mandatory Objectify domain class registration into a static initializer here since we’re not using a DAO at all.

As I mentioned at the beginning, this is not quite as satisfying (or typesafe) as a parameterized version of the Action, Result, and Handler classes. The difficulty with parameterizing these types is that the generic domain class parameter T has to propagate all the way up to ActionHandler<T>. You can’t bind a parameterized ActionHandler in gwt-dispatch because Guice doesn’t allow it; rather, you would need a concrete ActionHandler for each type T, which would defeat the purpose of having a generic Action/Result/Handler in the first place.

As always, YMMV.

15 Responses to ““Generic” Objectified ActionHandler for gwt-dispatch”

  1. Scott H said

    Well, I can see the use for this as a framework method, but now I can edit all of your data from your web-app (at least the kinds you have registered). Where is the url I should point my browser to? I’ll be sure to anonymous proxy server so there is no trace.

    I’d love to offer a suggestion that keeps with working (with some level of security), but well, I can’t.

    -Scott

    • Excellent concerns, Scott. I’m not actually using this approach currently because there is enough custom business logic in each DAO (including user permission checks) that I don’t have the need for it. I didn’t mention here that I do have all my gwt-dispatch Actions protected using a SecureDispatchServlet as I’ve written about at https://turbomanage.wordpress.com/2009/10/07/calling-appengine-securely-from-gwt-with-gwt-dispatch/, so only authenticated users who can spoof GWT-RPC would be able to do what you suggest. For a large public app, there are probably plenty of folks who can do that. But I don’t think folks writing an “intranet” app for a small Google Apps domain would have much to worry about as long as they’re likewise using the SecureDispatchServlet from gwt-dispatch.

    • One additional idea is that a generic Handler could verify that an ownerKey property on each Object (many entities denormalized for AppEngine Datastore will have such a property) matches the user associated with the session. That way, malicious users could only manipulate their own data. The Handler could access the property using reflection similar to how Objectify itself directly accesses entity fields.

      • Scott H said

        So that means I can grant myself admin privs (assuming you use a permission system based on the datastore user object).

        Yeah, the secure stuff is good to keep people out who don’t have accounts, but users can reek havoc.

        Maybe the next killer hacking tool is a web browser extension that takes apart the gwt generated RPCs (anything that get/posts data), and simply lets you call those RPC methods.

  2. Christian Goudreau said

    Well, I’m not gonna speak about security issues. But I was wondering If you could have an exemple without GAE backend and with JDO instead of objectify. There’s a lot of exemple how to do that with GAE, but I can’t find anything complete that satisfy my needs.

    I never used JDO before, so every times I find something about GWT and JDO or JPA without GAE, they assume that we used JDO before and it’s always something GWT 1.6 and older.

    It would be nice to have something similare to what you have done here with what I said in mind.

    BTW, really nice works, I come here from times to times to see your post and it’s always really well written and with simple and good explanation.

    Thanks

    • Thanks, Christian. There isn’t anything AppEngine-specific in JDO itself, as it’s a standard Java API. Many examples here use Guice, but that’s also open source and you can use it outside AppEngine. I’ve moved from JDO to Objectify (which *is* AppEngine-specific), so likely won’t be posting much further on JDO, but I can surmise that part of your difficulty finding stuff is because JPA has become much more popular than JDO for relational database access, thanks largely to Hibernate (a JPA implementation). If JPA is an option for your project, you’ll find a lot more written on it, such as Java Persistence with Hibernate (Manning Press). Hope that helps a little.

  3. Dan Billings said

    The security issue is certainly something to consider, but it should not preclude this (very) useful service.

    Wouldn’t a non-generic DAO-like service need to be secured in the same way, even though it only deals with one type? Furthermore, couldn’t you do server-side authentication and authorization to ensure that this user has the right do what he’s doing?

    I guess in short, the security issues exist regardless of what your service looks like. I think this is a great start. I intend to wrap this action with generic methods to get around the type-safety issue.

    Thanks again David!

    • You’re welcome, Dan. Yes, security issues exist regardless, but permission checking is often entity-specific so use of a universal ActionHandler like this one likely means you’re not verifying that a user has permission to modify a given entity. Bottom line: you need more code than what’s shown here. I’ll be curious to see what you come up with as far as generics.

  4. […] “Generic” Objectified ActionHandler for gwt-dispatch […]

  5. Dan Billings said

    Continuing my thoughts…

    I would argue that any service would have to do server-side security, because you can’t trust the user. Thus, this service would not need to be weighted down by some sort of security context from the client because it could just be spoofed.

    So I guess next step is generic CRUD security!

    • Right. But the ActionHandler in this example *does* run on the server, and doesn’t do any permission checking. Hence the need for SecureDispatchServlet to authenticate users and/or generic CRUD security, as you suggest. My gut is that you’ll have to restrict CRUD operations on at least one entity (the one that store user permissions) to AppEngine admins. And you would have to do this in the generic ActionHandler.

      • Dan Billings said

        Good point about a generic class needing to address the special case of permission classes.

        Along these lines, do you think the argument is valid that since you would never send a (custom) Permission over the wire to the client, you could reasonably never receive one as an argument? Or could a user spoof it anyway, assuming he has no idea what it looks like?

      • In general, I never recommend “security by obscurity.” This is especially true when using any flavor of RPC on the Web because your entire domain model is typically discoverable in JavaScript (WhiteHat Security has a paper on this, I believe). Better to specifically prohibit modification of that entity class in a generic handler: if (object instanceof UserPermission) {call Internet security police}

      • Scott H said

        This is a nice feature to play around with until you decide what your RPC interfaces should be. It gives you the advantage of writing arbitrary GWT client code (that works directly with objects) as you play around with ideas and features.

        You could write generic application authorization logic that makes sure you should not be able to put your permissions object, or remove (or change) your email address to something else (to spoof or gain other access) but why not just create explicit RCP methods at that point? They will be more explicit, maintainable and well documented; and there is the nice part about separation of concerns and the security that comes with validating those RPC calls.

        I hope you don’t mind my comments of concern but these are pretty dangerous things to enable. It is akin to writing a page where you can type SQL in and get results back. Sure you can restrict that SQL a bit, but once you open up a generic hole like that is very hard to lock-down. This, of course, isn’t that bad; you don’t have another language in which you can embed ever more logic.

      • I sincerely appreciate your comments, Scott. You made a very important point, if a little sarcastic at first 🙂 I wouldn’t want any reader to deploy this code “as is” without thinking through the security implications.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: