views:

184

answers:

1

I'm trying to combine Google App Engine with RPX Now user authentication and a per-user limited access pattern.

The per user access-limiting pattern relies upon GAE's global User.get_current_user(), like-so:

from google.appengine.api import users

class CurrentUserProperty(db.UserProperty):
  def checkCurrentUser(self, value):
    if value != users.get_current_user():
      raise db.BadValueError(
          'Property %s must be the current user' % self.name)
    return value

  def __init__(self, verbose_name=None, name=None):
    super(CurrentUserProperty, self).__init__(
        verbose_name, name, required=True, 
        validator=self.checkCurrentUser)

However, with GAE Utilities' sessions storing RPX's user identifier, there is no global User.

The most obvious solution to me is to create some global User variable (somehow localized to the current request/thread) in the middleware. However, I wouldn't do this unless I was confident there were no race conditions.

Is there any way to get the identity of the current user (as stored in the request session variable) to the CurrentUserProperty when CurrentUserProperty is constructed?

Thank you for reading.

EDIT:

Reading GAE's google/appengine/tools/dev_appserver.py:578 which does a:

579 env['USER_ID'] = user_id

and google/appengine/api/users.py:92ff which reads:

92  if _user_id is None and 'USER_ID' in os.environ:
93     _user_id = os.environ['USER_ID']

seems to suggest that you can safely set the environment in a single Google App Engine request (but I may be wrong!).

Presumably, then, I can set an environment variable in middleware. It smells of trouble, so I'd like to know if anyone else has (similarly) tackled this.

+1  A: 

App Engine instances (and indeed, CGI in general) are guaranteed to be single-threaded, so setting an environment variable per request is indeed safe - in fact, all the information about the current request is passed in through the current environment! Just make sure that you _un_set the environment variable if the user is not logged in, so you can't have an unauthenticated request get the authentication of the previous request to hit the same instance.

Nick Johnson
WSGI applications are NOT 'in general, guaranteed to be single-threaded'. It may be the case for WSGI under App Engine, but not elsewhere. There is even a flag in the WSGI environment called 'wsgi.multithread'. That this even exists is a hint that it can be operating within the context of a process handling concurrent requests using threads. As such, setting a process level environment variable in os.environ is dangerous and not portable.
Graham Dumpleton
Based on Nick Johnson's statement that a GAE request is guaranteed to be single-threaded (or at least, that's how I read it), GAE's use of the environment to hold user identities, and my experience with GAE, but noting Graham Dumpleton's reservation that this isn't a portable solution, this answers my question.
Brian M. Hunt
My mistake - WSGI can indeed be multithreaded. I've updated my answer to reflect that.
Nick Johnson