Question

I'm searching for some hints on how to implement row level access into a google app engine application using ndb hooks. As far as I know there is no out of the box authorization available, so I did add three attributes to the model: can_put, can_get and can_delete. Then I did add three ndb hooks: _pre_put_hook(), _pre_get_hook() and _pre_delete_hook().

PUT does work, but GET raises a RuntimeError (maximum recursion).

How can I get past the maximum recursion issue?

So far I do have got the following model:

import logging
import endpoints
from google.appengine.ext import ndb

class Location(ndb.Model):
    name = ndb.StringProperty(required=True)
    description = ndb.TextProperty()
    address = ndb.StringProperty(required=True)
    can_put = ndb.UserProperty(repeated=True)
    can_get = ndb.UserProperty(repeated=True)
    can_delete = ndb.UserProperty(repeated=True)
    created = ndb.DateTimeProperty(auto_now_add=True)
    modified = ndb.DateTimeProperty(auto_now=True)

def _pre_put_hook(self):
    current_user = endpoints.get_current_user()
    if not current_user:
        logging.debug("Location put: Invalid token.")
        raise endpoints.UnauthorizedException("Invalid token.")

    if self.key.id() is None:
        self.can_get.append(current_user)
        self.can_put.append(current_user)
        self.can_delete.append(current_user)
    else:
        location = self.key.get()
        if not current_user in location.can_put:
            logging.debug("Location put: Permission denied.")
            raise endpoints.ForbiddenException("Permission denied.")

@classmethod
def _pre_get_hook(cls, key):
    current_user = endpoints.get_current_user()
    if not current_user:
        logging.debug("Location get: Invalid token.")
        raise endpoints.UnauthorizedException("Invalid token.")

    location = key.get()
    if not current_user in location.can_get:
        logging.debug("Location get: Permission denied.")
        raise endpoints.ForbiddenException("Permission denied.")

@classmethod
def _pre_delete_hook(cls, key):
    current_user = endpoints.get_current_user()
    if not current_user:
        logging.debug("Location delete: Invalid token.")
        raise endpoints.UnauthorizedException("Invalid token.")

    location = key.get()
    if not current_user in location.can_delete:
        logging.debug("Location delete: Permission denied.")
        raise endpoints.ForbiddenException("Permission denied.")

But when I do run a GET operation through the API explorer I do get the following:

RuntimeError: maximum recursion depth exceeded while calling a Python object
Was it helpful?

Solution

You're calling key.get() inside the _pre_get_hook, which means it will be called recursively. That you're checking whether the user is in the can_put list, inside the get check suggests your logic is wrong, but checking against the can_get list will have to be done after the entity has been fetched, in _post_get_hook perhaps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top