2013-05-28 3 views
1

Я ищу помощь с моим приложением. Вот пример код первого (урезанный от около 2k линий ...), и я попытаюсь объяснить, что я ищу позже:ndb модели, декораторы, вложенные функции

from google.appengine.ext import ndb 
import webapp2 
import json 

class User(ndb.Model): 
    company_    = ndb.KeyProperty(repeated=True) 

    @property 
    def company(self): 
    return {} if not self.company_ else self.company_ 

    @company.setter 
    def company(self, value): 
    if value: 
     self.company_ = self.company_.expand(value) if self.company_ else [value] 
    else: 
     self.company_ = [] 
    self.put() 

class Company(ndb.Model): 
    administrator   = ndb.KeyProperty(kind=User, repeated=True) 
    manager    = ndb.KeyProperty(kind=User, repeated=True) 

    # FAKE decorator 
    @staticmethod 
    def administrator(handler): 
    def check_requirements(self, *a, **kw): 
     if True: 
     return 
     else: 
     return handler(self, *a, **kw) 
    return check_requirements 

class BaseHandler(webapp2.RequestHandler): 
    def jwrite(self, **kw): 
    return self.response.out.write(json.dumps(kw)) 

class require(BaseHandler): 
    @staticmethod 
    def login(handler): 
    def check_requirements(self, *a, **kw): 
     if not self.auth.get_user_by_session(): 
     self.redirect('/', abort=True) 
     else: 
     return handler(self, *a, **kw) 
    return check_requirements 

class ApiHandler(BaseHandler): 
    @require.login 
    def post(self, model, action, key=''): 
    method = '_post_%s' % model 
    try: 
     getattr(self, method)(action, key) 
    except Exception as error: 
     return self.jwrite(error = error) 

    def _post_company(self, action, key): 

    if action == 'create': 
     data = dict(self.request.POST) 
     """ Company.create(data) method: 
      Populates Company instance with POST data. 
      Assigns first user that created the company 
      both administrator and manager roles. 
     """ 
     key_ = Company.create(data) 
     if key_: 
     self.user.company = key_ 
     return 

    elif action == 'delete': 

     @Company.administrator 
     def delete_all_user_companies(self): 
     ndb.delete_multi(self.user.company) 
     self.user.company = None 
     return 

     companies = ndb.get_multi(self.user.company) 
     if self.user.key in map(lambda c: c.administrator, companies): 
     delete_all_user_companies(self) 

    elif action == 'update': 

     @Company.manager 
     def update_company(self, key): 
     data = dict(self.request.POST) 
     """ Company.update(key, data) method: 
      Populates Company instance with POST data 
     """ 
     key_ = Company.update(key, data) 
     if key_: 
      return 

     company = ndb.Key(Company, key).get() 
     if self.user.key in company.manager.extend(company.administrator): 
     update_company(self) 

Как вы можете видеть, у меня есть пользователи и Компания модель. Пользователь может иметь несколько компаний и компаний, которые могут иметь несколько пользователей, которые могут быть администраторами или менеджерами. Вы заметите некоторые декораторы и вложенные функции - большинство из них поддельные (но это то, что я ищу ...

Я делаю базовую регистрационную проверку с @ require.login decorator (I ' я поместил его в отдельный класс, просто потому, что он выглядит более чистым в коде - @ require.login vs @ BaseHandler.require_login). С его помощью я «защитил» почтовый метод моего API, и теперь мне нужно выполнить дополнительную проверку роли - администраторы могут делать некоторые вещи, которые не могут выполнять менеджеры. Мне нужно будет сделать эту проверку в нескольких других местах, поэтому я думаю, что это будет хорошее место для декораторских функций, но я не могу понять, как их писать. :

  1. Что было бы хорошим местом для этого декоратора? Должен ли я помещать его в класс компании или где-то в классе ApiHandler? Мой первый инстинкт заключается в том, чтобы поместить его в класс компании, но я не уверен, как справиться с областью - мне нужно получить экземпляр пользователя (список self.user.company) там как-то ...

  2. Следующая вещь менеджер декоратор. Как я пишу это как декоратор:

    company = ndb.Key(Company, key).get() 
        if self.user.key in company.manager.extend(company.administrator): 
        update_company(self) 
    

    и использовать его в качестве @ Company.manager или @ requre.manager, в зависимости от ответа на мой первый вопрос?

  3. Другой декоратор для администратора, что это немного сложнее - я должен проверить, если пользователь администратором для всех его компаний, а также удалять те, где он, сохраняя при этом те, где он не является:

    companies = ndb.get_multi(self.user.company) 
        if self.user.key in map(lambda c: c.administrator, companies): 
        delete_all_user_companies(self) 
    

    Я даже не уверен, правильна ли эта функция map(), и если код будет работать, еще не пробовал - это просто псевдозапись на данный момент ...

  4. Последний вопрос: Я беспокоюсь о взломе запроса POST? Исходя из приведенного выше примера кода, возможно ли, что некоторый пользователь может создавать пользовательские POST-запросы и удалять или обновлять компанию, а не его? !

Любая помощь, комментарии или идеи будут оценены (; спасибо

ответ

3

Я считаю, что я решил это:

from google.appengine.ext import ndb 
import webapp2 
import json 

class User(ndb.Model): 
    company_    = ndb.KeyProperty(repeated=True) 

    @property 
    def company(self): 
    return {} if not self.company_ else self.company_ 

    @company.setter 
    def company(self, value): 
    if value: 
     # self.company_ = self.company_.expand(value) if self.company_ else [value] 
     # Lists mutate when expanded. Code above was returning None 
     self.company_ = self.company_ + [value] if self.company_ else [value] 
    else: 
     self.company_ = [] 
    self.put() 

class Company(ndb.Model): 
    administrator   = ndb.KeyProperty(kind=User, repeated=True) 
    manager    = ndb.KeyProperty(kind=User, repeated=True) 

class BaseHandler(webapp2.RequestHandler): 
    def jwrite(self, **kw): 
    return self.response.out.write(json.dumps(kw)) 

class require(BaseHandler): 
    @staticmethod 
    def login(handler): 
    def check_requirements(self, *a, **kw): 
     if not self.auth.get_user_by_session(): 
     self.redirect('/', abort=True) 
     else: 
     return handler(self, *a, **kw) 
    return check_requirements 

class role(BaseHandler): 
    @staticmethod 
    def administrator(handler): 
    def check_requirements(self, *a, **kw): 
     # I didn't care much about optimizing queries 
     # since this isn't frequent operation. 
     # For more frequent calls, I'd consider projections. 
     companies = ndb.get_multi(*a) 
     # Next lines checks if current user is administrator 
     # for all companies passed to the function 
     if not self.user.key in reduce(lambda x, y: x if x != y else y, map(lambda c: c.administrator, companies)): 
     return self.jwrite(error = 'Permission denied. Administrator required.') 
     else: 
     return handler(self, *a, **kw) 
    return check_requirements 

    @staticmethod 
    def manager(handler): 
    def check_requirements(self, *a, **kw): 
     companies = ndb.get_multi(*a) 
     # Next lines checks if current user is manager 
     # or administrator (since admin has higher privileges) 
     # for all companies passed to the function 
     if not self.user.key in reduce(lambda x, y: x if x != y else y, map(lambda c: c.manager + c.administrator, companies)): 
     return self.jwrite(error = 'Permission denied. Manager or Administrator required.') 
     else: 
     return handler(self, *a, **kw) 
    return check_requirements 

class ApiHandler(BaseHandler): 
    @require.login 
    def post(self, model, action, key=''): 
    method = '_post_%s' % model 
    try: 
     getattr(self, method)(action, key) 
    except Exception as error: 
     return self.jwrite(error = error) 

    def _post_company(self, action, key): 

    if action == 'create': 
     data = dict(self.request.POST) 
     """ Company.create(data) method: 
      Populates Company instance with POST data. 
      Assigns first user that created the company 
      both administrator and manager roles. 
     """ 
     key_ = Company.create(data) 
     if key_: 
     self.user.company = key_ 
     return 

    elif action == 'delete': 

     @role.administrator 
     def delete_all_user_companies(self, *a): 
     ndb.delete_multi(*a) 
     self.user.company = None 
     return 

     delete_all_user_companies(self, self.user.company) 

    elif action == 'update': 

     @role.manager 
     def update_company(self, *a): 
     data = dict(self.request.POST) 
     """ Company.update(key, data) method: 
      Populates Company instance with POST data 
     """ 
     key_ = Company.update(key, data) 
     if key_: 
      return 

     update_company(self, ndb.Key(Company, key)) 

и ответить на мои собственные вопросы:

  1. У меня были проблемы с наименованием внутри класса компании - там был администратор недвижимости и декоратор с тем же именем. Поэтому я переместил deco rators для API, в новый класс (роль) для удобства.Когда я писал декораторов, я понял, что могу использовать их для любых других моделей (с полями администратора и администратора), поэтому я думаю, что это был хороший вызов (;

  2. Написание декораторов заняло некоторое время и экспериментировало с отображением и уменьшением массивов, но Мне удалось это сделать. Я не уверен в передаче аргументов декоратору tho '. Может быть, я должен делать запросы за пределами декораторов? Или передать согласованные элементы функции обработчика? Мне нужно будет изучить это. .

  3. ... и удалять те, где он, сохраняя при этом те, где он не является администратором Вот почему я сделал запросы внутри декоратора в первую очередь, но до сих пор не уверен, если это умный (;.

  4. Я все еще мог бы использовать ответ на этот вопрос.

Надеется, что это помогает кто-то ...

+0

Это нормально принять на свой вопрос! –

Смежные вопросы