我正在为我的应用寻找帮助。首先是示例代码(从大约2k行中删除......),然后我会尝试解释我以后要查找的内容:
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)
如您所见,我有User和Company模型。用户可以拥有多个公司,公司可以拥有多个用户,可以是管理员或经理。您会注意到一些装饰器和嵌套功能 - 其中大多数都是假的(;但那是我正在寻找的......
我正在使用 @ require.login 装饰器进行基本的登录检查(我已将它放在单独的类中,因为它在代码中看起来更干净 - @ require.login vs @ BaseHandler.require_login)。有了它,我已经安全了#34;我的API的post方法,现在我需要对角色进行额外的检查 - 管理员可以做一些管理员不能做的事情。我需要在其他几个地方进行检查,所以我认为这对于装饰器功能来说是一个好地方,但我无法弄清楚如何编写它们。我的第一个问题是:
这个装饰师会有什么好处?我应该将它放在公司课程中还是ApiHandler课程的某个地方?我的第一直觉是将它放在公司课程中,但我不确定如何处理范围 - 我需要以某种方式获取用户实例(self.user.company list)...... / p>
接下来是经理装饰。我怎么把它写成装饰者:
company = ndb.Key(Company, key).get()
if self.user.key in company.manager.extend(company.administrator):
update_company(self)
并将其用作 @ Company.manager 或 @ requre.manager ,具体取决于我对第一个问题的回答?
管理员的另一个装饰者有点复杂 - 我必须检查用户是否是他所有公司的管理员,并删除他所在的位置,同时保留他所在的位置ISN' T:
companies = ndb.get_multi( self.user.company )
if self.user.key in map( lambda c: c.administrator, companies ):
delete_all_user_companies(self)
我甚至不确定这个map()函数是否正确,如果代码可行,但还没有尝试 - 它现在只是一个伪代码占位符。
最后一个问题:我应该关注POST请求黑客攻击吗?根据上面的示例代码,某些用户是否可以制作自定义POST请求并删除或更新不属于他的公司?
任何帮助,评论或见解将不胜感激(;谢谢!
答案 0 :(得分: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))
并回答我自己的问题:
我在公司类中命名存在问题 - 有属性管理员和装饰器具有相同的名称。因此,为了方便起见,我将装饰器移动到API,进入新类(角色)。当我写装饰器时,我意识到我可以将它们用于任何其他模型(具有管理员和管理员字段),所以我想这是一个很好的电话(;
编写装饰器花了一些时间并试验映射和减少数组,但我已设法拉它。我不确定是否将参数传递给装饰者。也许我应该在装饰者外做查询?或者将匹配的项目传递给处理函数?我将不得不调查它......
...并删除他所在的位置,同时保留他不是管理员的位置这就是我首先在装饰器内部进行查询的原因。但仍然不确定它是否聪明(;
我仍然可以使用这个答案。
希望这有助于某人...