Is there a way to handle requests with locale-dependant wsgi handlers? - google-app-engine

Is there a way to handle requests from different geographic locations with a different WSGI handler? Specifically I want to allow all requests from one local (US) and redirect all others to a holding page i.e. something like
application_us = webapp2.WSGIApplication([
('/.*', MainHandler)
], debug=DEBUG)
application_elsewhere = webapp2.WSGIApplication([
('/.*', HoldingPageHandler)
], debug=DEBUG)
I'm aware of X-AppEngine-Country however I'm not quite sure how to put it to use in this case?

Okay, building the answer by Sebastian Kreft I figured it's probably easiest to throw this into a base handler of which every other handler is a subclass as follows.
class BaseHandler(webapp2.RequestHandler):
def __init__(self, *args, **kwargs):
super(BaseHandler, self).__init__(*args, **kwargs)
country = self.request.headers.get('X-AppEngine-Country')
if not country == "US" and not country == "" and not country == None: # The last two handle local development
self.redirect('international_holding_page')
logging.info(country)
This is more in keeping with DRY though I'm not certain it's the most efficient way of doing this.

What you need to do is to have only one app and in the handler redirect the user to a HoldingPageHandler if the country is not supported.
See Is it possible to use X-AppEngine-Country within an application. There they explain how to get the country
country = self.request.headers.get('X-AppEngine-Country')
So your handler would be something like this
class MainHandler(webapp2.RequestHandler):
def get(self):
country = self.request.headers.get('X-AppEngine-Country')
if country != "US":
self.redirect_to('hold') #assuming you have a route to hold
# your logic

Related

How can I manage authorization in GAE using Google Accounts?

So far I have used oauth2 to manage authentication using Google Accounts for my app, which gives me some data to complete a basic profile. The problem is that I now want to manage permissions to view and edit a lot of the content in the app, with different groups of people being able to view/edit different parts of the app.
I want some parts of my application to be accessed by users with permission for A, some for B, C, etc. The way I started doing this was using a decorator in the get and post method of each handler, like this:
class SomeHandler(Handler):
#validate_access
def get(self):
pass
#validate_access
def post(self):
pass
Where #validate_access executes the code in the function only if the user has permission for it, and returning an authorization error if not. This seemed to be a good solution a while back, but since there are many handlers I have to use that decorator everywhere, which is annoying and dangerous, since I may forget to put it in some functions.
Is there a way to put this validations in the initialization of the base handler, so that I don't have to use that decorator everywhere? I imagine something like this:
class BaseHandler(webapp2.RequestHandler):
def initialize(self, request, response):
super(Handler, self).initialize(request, response)
self.user = users.get_current_user()
employee = Employee.query(user_id=self.user.user_id).get()
if employee.auth_level > 3:
#See the content: Do whatever the "get" method of the corresponding handler does.
pass
else:
#Raise authorization error
pass
Or is there a better way to do this? (Sorry if it's a basic question, I've never done this before)
Yes, you can overwrite the webapp2 dispatch handler for this purpose. I used this method to enforce role based access control (RBAC).
Example code:
class BaseHandler(webapp2.RequestHandler):
""" webapp2 base handler """
def dispatch(self):
# UserAccess aborts if the user does not have permission to use a handler
UserAccess(self.request)
super(BaseHandler, self).dispatch()
....
class ExampleHandler(BaseHandler):
def get(self):
.....
I use a config file with the allowed roles for a handler. This file is also used to create the webapp2 routes and the dynamic user menu.

Passing credentials to a task

Hopefully someone can help. I am building an app on Google app engine and trying to pass the credentials of an authenticated user to the push task Handler. I am using the OAuth2DecoratorFromClientSecrets library to create the decorator which seems to store the credentials for the user in the datastore. It stores it with a key name of something like "110111913122157971566". My problem is that I cant seem to find a way to figure out what that key name is so that I can retrieve it using the StorageByKeyName method from within my worker handler. The documentation I have read uses the user_id as the key name but this doesnt work for me as the credentials are not stored with the user_id as the key name, however if I hard code the key name then the code does work. I am aware that I could run the copy code from within the Submit handler but need to run it as a separate task. Below is a sample of my code, thanks for any help you can provide:
JINJA_ENVIRONMENT = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
extensions=['jinja2.ext.autoescape'],
autoescape=True)
SCOPES =['https://www.googleapis.com/auth/drive']
decorator = OAuth2DecoratorFromClientSecrets(
os.path.join(os.path.dirname(__file__),
'client_secrets.json'),
' '.join(SCOPES)
)
class MainPage(webapp2.RequestHandler):
def get(self):
if users.get_current_user():
url = users.create_logout_url(self.request.uri)
url_linktext = 'Logout'
template_values = {'url': url,
'url_linktext': url_linktext,
}
template = JINJA_ENVIRONMENT.get_template('index.html')
self.response.write(template.render(template_values))
else:
self.redirect(users.create_login_url(self.request.uri))
class Submit(webapp2.RequestHandler):
#decorator.oauth_required
def post(self):
taskqueue.add(url='/worker', params={'user_id' : users.get_current_user().user_id()})
self.response.write('<html><body>You wrote:<pre>')
self.response.write(users.get_current_user().user_id())
self.response.write('</pre></body></html>')
class Worker(webapp2.RequestHandler):
def post(self):
user_id = self.request.get('user_id')
credentials = StorageByKeyName(CredentialsModel, user_id , 'credentials').get()
http = httplib2.Http()
http = credentials.authorize(http)
service = build('drive', 'v2',http=http)
fileId = 'actual file_id of drive file here'
copied_file = {'title': 'My New Test Doc2'}
new_file = service.files().copy(fileId=fileId,body=copied_file).execute(http=http)
application = webapp2.WSGIApplication([
('/', MainPage),
('/submit', Submit),
('/worker', Worker),
(decorator.callback_path, decorator.callback_handler()),
], debug=True)
First, remove the http=decorator.http at the top level - it doesn't appear to be used and should only be used from inside the decorated method.
I'm not certain, but looking at the decorator code I think it is is keying based on the user_id(). Your code is displaying that, but the parameter you are passing to the task isn't. Try {'user_id' : users.get_current_user().user_id()}.

Why is an entity not being fetched from NDB's in-context cache?

I have an entity that is used to store some global app settings. These settings can be edited via an admin HTML page, but very rarely change. I have only one instance of this entity (a singleton of sorts) and always refer to this instance when I need access to the settings.
Here's what it boils down to:
class Settings(ndb.Model):
SINGLETON_DATASTORE_KEY = 'SINGLETON'
#classmethod
def singleton(cls):
return cls.get_or_insert(cls.SINGLETON_DATASTORE_KEY)
foo = ndb.IntegerProperty(
default = 100,
verbose_name = "Some setting called 'foo'",
indexed = False)
#ndb.tasklet
def foo():
# Even though settings has already been fetched from memcache and
# should be available in NDB's in-context cache, the following call
# fetches it from memcache anyways. Why?
settings = Settings.singleton()
class SomeHandler(webapp2.RequestHandler):
#ndb.toplevel
def get(self):
settings = Settings.singleton()
# Do some stuff
yield foo()
self.response.write("The 'foo' setting value is %d" % settings.foo)
I was under the assumption that calling Settings.singleton() more than once per request handler would be pretty fast, as the first call would most probably retrieve the Settings entity from memcache (since the entity is seldom updated) and all subsequent calls within the same request handler would retrieve it from NDB's in-context cache. From the documentation:
The in-context cache persists only for the duration of a single incoming HTTP request and is "visible" only to the code that handles that request. It's fast; this cache lives in memory.
However, AppStat is showing that my Settings entity is being retrieved from memcache multiple times within the same request handler. I know this by looking at a request handler's detailed page in AppStat, expanding the call trace of each call to memcache.Get and looking at the memcahe key that is being reteived.
I am using a lot of tasklets in my request handlers, and I call Settings.singleton() from within the tasklets that need access to the settings. Could this be the reason why the Settings entity is being fetched from memcache again instead of from the in-context cache? If so, what are the exact rules that govern if/when an entity can be fetched from the in-context cache or not? I have not been able to find this information in the NDB documentation.
Update 2013/02/15: I am unable to reproduce this in a dummy test application. Test code is:
class Foo(ndb.Model):
prop_a = ndb.DateTimeProperty(auto_now_add = True)
def use_foo():
foo = Foo.get_or_insert('singleton')
logging.info("Function using foo: %r", foo.prop_a)
#ndb.tasklet
def use_foo_tasklet():
foo = Foo.get_or_insert('singleton')
logging.info("Function using foo: %r", foo.prop_a)
#ndb.tasklet
def use_foo_async_tasklet():
foo = yield Foo.get_or_insert_async('singleton')
logging.info("Function using foo: %r", foo.prop_a)
class FuncGetOrInsertHandler(webapp2.RequestHandler):
def get(self):
for i in xrange(10):
logging.info("Iteration %d", i)
use_foo()
class TaskletGetOrInsertHandler(webapp2.RequestHandler):
#ndb.toplevel
def get(self):
logging.info("Toplevel")
use_foo()
for i in xrange(10):
logging.info("Iteration %d", i)
use_foo_tasklet()
class AsyncTaskletGetOrInsertHandler(webapp2.RequestHandler):
#ndb.toplevel
def get(self):
logging.info("Toplevel")
use_foo()
for i in xrange(10):
logging.info("Iteration %d", i)
use_foo_async_tasklet()
Before running any of the test handlers, I make sure that the Foo entity with keyname singleton exists.
Contrary to what I am seeing in my production app, all of these request handlers show a single call to memcache.Get in Appstats.
Update 2013/02/21: I am finally able to reproduce this in a dummy test application. Test code is:
class ToplevelAsyncTaskletGetOrInsertHandler(webapp2.RequestHandler):
#ndb.toplevel
def get(self):
logging.info("Toplevel 1")
use_foo()
self._toplevel2()
#ndb.toplevel
def _toplevel2(self):
logging.info("Toplevel 2")
use_foo()
for i in xrange(10):
logging.info("Iteration %d", i)
use_foo_async_tasklet()
This handler does show 2 calls to memcache.Get in Appstats, just like my production code.
Indeed, in my production request handler codepath, I have a toplevel called by another toplevel. It seems like a toplevel creates a new ndb context.
Changing the nested toplevel to a synctasklet fixes the problem.
It seems like a toplevel creates a new ndb context.
Exactly, each handler with a toplevel decorator have its own context and therefore a separate cache. You can take a look to the code for toplevel in the link below, in the function documentation states that toplevel is "A sync tasklet that sets a fresh default Context".
https://code.google.com/p/googleappengine/source/browse/trunk/python/google/appengine/ext/ndb/tasklets.py#1033

How can I pass data to a GAE base template?

Using Google App Engines webapp framework, is there any way to pass data to a base template?
To be specific, I just want a logout button to be visible if the user is logged on (using googles own authentication system).
I'm still learning so I'm not sure what parts are GAE specific and what parts are django specific; having to send the logged in user from every single request handler seems very un-DRY.
Arguments to base templates are passed the same way as any other template arguments, by being passed to template.render. I usually solve this by having a convenience method on my base handler that inserts common template arguments, like this:
class BaseHandler(webapp.RequestHandler):
def render_template(self, filename, template_args):
path = os.path.join(os.path.dirname(__file__), 'templates', filename)
template_args.update({
'user': users.get_current_user(),
# ...
})
class MyHandler(BaseHandler):
def get(self):
self.render_template('my.html', {'foo': 'bar'})
I think you are looking for something like login_required decorator in django. You can either try to use a complete django framework in GAE (I never tried) or it can be customized easily with decoration and add your own behavior. In your case, it will be a good idea to pass user's login status to template engine.
#the decorator
def login_checked(f):
def wrap(request, *args, **kwargs):
# get current user
user = get_current_user()
template_path, vars = f(request, *args, **kwargs)
vars['user']= user
template.render(template_path, vars)
return wrap
# usage
class MyPage(webapp.RequestHandler):
#login_checked # add a decoration
def get(self):
# your page
return "the_template_page_you_want", {"the value you want to pass to template": "xxx"}
Take a look at this example:
from google.appengine.api import users
class MyHandler(webapp.RequestHandler):
def get(self):
user = users.get_current_user()
if user:
greeting = ("Welcome, %s! (sign out)" %
(user.nickname(), users.create_logout_url("/")))
else:
greeting = ("Sign in or register." %
users.create_login_url("/"))
self.response.out.write("<html><body>%s</body></html>" % greeting)
Source: http://code.google.com/appengine/docs/python/users/loginurls.html

Override save, put, get, etc... methods in Google App Engine

Is it possible to override methids for db.Model in Google App Engine?
I want to declare beforeSave, afterSave methods, etc.. to create automatic tagging system.
I know there are hooks, but it seems to me a wrong way to solve this issue :)
Thanks!
Yes, it's possible to override these methods. Have a look at this blog post by Nick Johnson.The hooked model class looks this:
class HookedModel(db.Model):
def before_put(self):
pass
def after_put(self):
pass
def put(self, **kwargs):
self.before_put()
super(HookedModel, self).put(**kwargs)
self.after_put()
Read the blog to see how to handle the db.put() method too.
You might also be interested on "derived properties".
I posted an extension to jbochi's HookedModel class so that the before_put and after_put methods are correctly invoked when called from db.put() and the _async family of functions.
See AppEngine PreCall API hooks
I've attempted to improve upon the answer by #jbochi:
According to https://developers.google.com/appengine/docs/python/datastore/modelclass#Model_put , put() should return a Key, so the return value from the library should be passed through.
db.Model.save(), while deprecated, is (a) sometimes still used, (b) meant to be a synonym for put() and (c) apparently not called directly by put() - so should be handled manually.
Revised code:
class HookedModel(db.Model):
def before_put(self):
pass
def after_put(self):
pass
def put(self, **kwargs):
self.before_put()
result = super(HookedModel, self).put(**kwargs)
self.after_put()
return result
def save(self, **kwargs):
self.before_put()
result = super(HookedModel, self).save(**kwargs)
self.after_put()
return result
You should still read http://blog.notdot.net/2010/04/Pre--and-post--put-hooks-for-Datastore-models if you wish to use the monkeypatching, or Chris Farmiloe's answer for use of the async methods.

Resources