google appengine 数据存储区查询的行级访问权限
Row level access for google appengine datastore queries
我正在尝试开发对 google appengine 数据存储表的行级访问。到目前为止,我确实有一个使用 _hooks 的常规 ndb put()、get() 和 delete() 操作的工作示例。
class Acl 应由所有其他表使用。它用作结构化 属性.
class Acl(EndpointsModel):
UNAUTHORIZED_ERROR = 'Invalid token.'
FORBIDDEN_ERROR = 'Permission denied.'
public = ndb.BooleanProperty()
readers = ndb.UserProperty(repeated=True)
writers = ndb.UserProperty(repeated=True)
owners = ndb.UserProperty(repeated=True)
@classmethod
def require_user(cls):
current_user = endpoints.get_current_user()
if current_user is None:
raise endpoints.UnauthorizedException(cls.UNAUTHORIZED_ERROR)
return current_user
@classmethod
def require_reader(cls, record):
if not record:
raise endpoints.NotFoundException(record.NOT_FOUND_ERROR)
current_user = cls.require_user()
if record.acl.public is not True or current_user not in record.acl.readers:
raise endpoints.ForbiddenException(cls.FORBIDDEN_ERROR)
我确实想保护对位置 class 的访问。所以我确实在 class.
中添加了三个钩子(_post_get_hook、_pre_put_hook 和 _pre_delete_hook)
class Location(EndpointsModel):
QUERY_FIELDS = ('state', 'limit', 'order', 'pageToken')
NOT_FOUND_ERROR = 'Location not found.'
description = ndb.TextProperty()
address = ndb.StringProperty()
acl = ndb.StructuredProperty(Acl)
@classmethod
def _post_get_hook(cls, key, future):
location = future.get_result()
Acl.require_reader(location)
def _pre_put_hook(self):
if self.key.id() is None:
current_user = Acl.require_user()
self.acl = Acl()
self.acl.readers.append(current_user)
self.acl.writers.append(current_user)
self.acl.owners.append(current_user)
else:
location = self.key.get()
Acl.require_writer(location)
这适用于所有创建、读取、更新和删除操作,但不适用于查询。
@Location.query_method(user_required=True,
path='location', http_method='GET', name='location.query')
def location_query(self, query):
"""
Queries locations
"""
current_user = Acl.require_user()
query = query.filter(ndb.OR(Location.acl.readers == current_user, Location.acl.public == True))
return query
当我 运行 查询所有位置时,我收到以下错误消息:
BadArgumentError: _MultiQuery with cursors requires __key__ order
现在我有一些问题:
- 如何解决 _MultiQuery 问题?
- 修复后:这个 Acl 实现有意义吗?有开箱即用的替代品吗? (我想将 Acl 存储在记录本身上,以便能够 运行 直接查询,而不必先获取密钥。)
Datastore 本身不支持 OR
过滤器。相反,NDB 在幕后所做的是 运行宁两个查询:
query.filter(Location.acl.readers == current_user)
query.filter(Location.acl.public == True)
然后它将这两个查询的结果合并到一个结果集中。为了正确合并结果(特别是当你有重复的属性时消除重复),从任意位置(使用游标)继续查询时,查询需要按键排序。
为了 运行 查询成功,您需要在 运行 查询之前附加一个键顺序到查询:
def location_query(self, query):
"""
Queries locations
"""
current_user = Acl.require_user()
query = query.filter(ndb.OR(Location.acl.readers == current_user,
Location.acl.public == True)
).order(Location.key)
return query
不幸的是,您的 ACL 实现不适用于查询。特别是,查询结果不会调用 _post_get_hook
。有个bug filed on the issue tracker about this.
我正在尝试开发对 google appengine 数据存储表的行级访问。到目前为止,我确实有一个使用 _hooks 的常规 ndb put()、get() 和 delete() 操作的工作示例。
class Acl 应由所有其他表使用。它用作结构化 属性.
class Acl(EndpointsModel):
UNAUTHORIZED_ERROR = 'Invalid token.'
FORBIDDEN_ERROR = 'Permission denied.'
public = ndb.BooleanProperty()
readers = ndb.UserProperty(repeated=True)
writers = ndb.UserProperty(repeated=True)
owners = ndb.UserProperty(repeated=True)
@classmethod
def require_user(cls):
current_user = endpoints.get_current_user()
if current_user is None:
raise endpoints.UnauthorizedException(cls.UNAUTHORIZED_ERROR)
return current_user
@classmethod
def require_reader(cls, record):
if not record:
raise endpoints.NotFoundException(record.NOT_FOUND_ERROR)
current_user = cls.require_user()
if record.acl.public is not True or current_user not in record.acl.readers:
raise endpoints.ForbiddenException(cls.FORBIDDEN_ERROR)
我确实想保护对位置 class 的访问。所以我确实在 class.
中添加了三个钩子(_post_get_hook、_pre_put_hook 和 _pre_delete_hook)class Location(EndpointsModel):
QUERY_FIELDS = ('state', 'limit', 'order', 'pageToken')
NOT_FOUND_ERROR = 'Location not found.'
description = ndb.TextProperty()
address = ndb.StringProperty()
acl = ndb.StructuredProperty(Acl)
@classmethod
def _post_get_hook(cls, key, future):
location = future.get_result()
Acl.require_reader(location)
def _pre_put_hook(self):
if self.key.id() is None:
current_user = Acl.require_user()
self.acl = Acl()
self.acl.readers.append(current_user)
self.acl.writers.append(current_user)
self.acl.owners.append(current_user)
else:
location = self.key.get()
Acl.require_writer(location)
这适用于所有创建、读取、更新和删除操作,但不适用于查询。
@Location.query_method(user_required=True,
path='location', http_method='GET', name='location.query')
def location_query(self, query):
"""
Queries locations
"""
current_user = Acl.require_user()
query = query.filter(ndb.OR(Location.acl.readers == current_user, Location.acl.public == True))
return query
当我 运行 查询所有位置时,我收到以下错误消息:
BadArgumentError: _MultiQuery with cursors requires __key__ order
现在我有一些问题:
- 如何解决 _MultiQuery 问题?
- 修复后:这个 Acl 实现有意义吗?有开箱即用的替代品吗? (我想将 Acl 存储在记录本身上,以便能够 运行 直接查询,而不必先获取密钥。)
Datastore 本身不支持 OR
过滤器。相反,NDB 在幕后所做的是 运行宁两个查询:
query.filter(Location.acl.readers == current_user)
query.filter(Location.acl.public == True)
然后它将这两个查询的结果合并到一个结果集中。为了正确合并结果(特别是当你有重复的属性时消除重复),从任意位置(使用游标)继续查询时,查询需要按键排序。
为了 运行 查询成功,您需要在 运行 查询之前附加一个键顺序到查询:
def location_query(self, query):
"""
Queries locations
"""
current_user = Acl.require_user()
query = query.filter(ndb.OR(Location.acl.readers == current_user,
Location.acl.public == True)
).order(Location.key)
return query
不幸的是,您的 ACL 实现不适用于查询。特别是,查询结果不会调用 _post_get_hook
。有个bug filed on the issue tracker about this.