我正在尝试在我的网站上实现金字塔的安全功能,但我在弄清楚如何使用它时遇到了一些麻烦。
我一直在阅读this tutorial和this example以及Pyramid文档,我无法弄清楚如何为单页ID实施授权策略。
例如,我有以下网址方案:
/pages
/pages/12
/pages
显然列出了可用的页面,/pages/:id
是您可以在页面上阅读/评论的位置。
我读过的文档/示例表明,您可以通过提供带有组列表的groupfinder
回调来实现组级ACS。例如editor
,admin
等
如何根据页面ID使用权限而不是权限?
在我上面的URL方案中,当用户浏览到/pages
时,他们必须登录。当他们浏览到/pages/:id
时,他们必须有权查看该特定ID。或者,他们必须是该页面的所有者。
与评论相同。在/page/:id
页面上,他们可能有权查看该页面但不对其进行评论。
答案 0 :(得分:6)
这里的基本原则是Pyramid的安全机制在当前上下文中检查ACL。在这种情况下,您的页面将是要使用的逻辑上下文。第一步是为页面设置上下文工厂。假设您正在使用SQLAlchemy和URL分派,这很容易做到。注册您的路线:
config.add_route('page', '/pages/{id:\d+}', factory=page_factory)
在路径的路径中有一个小技巧,使得金字塔检查页面ID必须是一个数字,因此您不必自己检查。请注意对* page_factory *方法的引用。让我们现在定义:
def page_factory(request):
return DBSession.query(Page).get(int(request.matchdict['id']))
这将获取路径中的页面ID,并使用它来查找数据库中的页面。请注意,我们不会检查id是否可以在这里转换为整数:我们可以自行解决,因为路由已经直接检查了。
下一步是在页面上设置ACL。最简单的方法是向Page class:
添加 acl 属性from pyramid import security
class Page(BaseObject):
@property
def __acl__(self):
return [(security.Allow, self.userid, 'view')]
此ACL告诉金字塔,只允许具有存储在page.userid中的id的用户查看该页面。这里要认识到的重要一点是每个页面的ACL都是不同的:它是根据数据库中的信息分别为每个页面生成的;在这种情况下使用self.userid。
您现在可以在视图中使用视图权限:
@view_config(route_name='page', context=Page, permission='view')
def page_view(context, request):
return 'I can see!'
此示例的页面ACL非常小,但您可以根据需要进行扩展。
还要注意view_config的context = Page参数:这告诉金字塔这个视图应该只用于上下文是一个Page。如果上下文工厂(此示例中为page_factory)未找到匹配的页面,则它将返回None而不是Page实例,因此金字塔不会使用此视图。因此,金字塔将自动产生未发现的错误。
答案 1 :(得分:3)
出于本讨论的目的,我将假设您正在使用SQLAlchemy与您的数据库进行交互。
如果config.add_route('pages', '/pages/{id}')
中有__init__.py
,则可以添加自定义工厂来替换/补充默认ACL。例如:
您当前的ACL可能如下所示:
class RootFactory(object):
__acl__ = [
(Allow, Everyone, 'view'),
(Allow, Authenticated, 'auth'),
]
def __init__(self, request):
self.request = request
这将允许经过身份验证的用户访问具有“auth”权限的任何视图,以及访问您网站以访问具有“查看”权限的任何视图的任何人。
使用自定义工厂,您可以绕过RootFactory,也可以对其进行补充。
要绕过,请将原始config.add_route更改为 - > config.add_route('pages', '/pages/{id}', factory=PageFactory)
并创建一个像这样的PageFactory类:
class PageFactory(object):
__acl__ = [
(Allow, Everyone, 'view'),
(Allow, Authenticated, 'auth'),
]
def __init__(self, request):
self.request = request
from pyramid.security import authenticated_userid
user_id = authenticated_userid(self.request)
thispage = DBSession.query(Page).filter(Page.id==self.request.matchdict['id']).first()
if thispage.user_id == user_id:
## Pyramid allows Everyone, Authenticated, and authenticated_userid
## (each of these is known as a Principal) to be in the second
## position of the ACL tuple
acl.append((Allow, user_id, 'edit'))
这假设您的观点有permission='edit'
作为其参数之一。
现在,如果您想将RootFactory和补充与自定义工厂一起使用,那么您不必重复自己,只需将RootFactory留作我在这篇文章的开头展示过,并继承自RootFactory类,如下:
class PageFactory(RootFactory):
@property
def __acl__(self):
acl = super(PageFactory, self).__acl__[:] ##[:] creates a copy
from pyramid.security import authenticated_userid
user_id = authenticated_userid(self.request)
thispage = DBSession.query(Page).filter(Page.id==self.request.matchdict['id']).first()
if thispage.user_id == user_id:
acl.append((Allow, user_id, 'edit'))
return acl
顺便说一句, groupfinder 非常非常有用,因为这样您就可以简单地将用户置于群组中,例如“admin”,以及管理员组中的所有用户可以使用您可能需要的permission='whatever'
或permission='whateverelse'
访问视图,并且不需要工厂,只能访问为当前用户返回组列表的组查找器。唉,我离题了,因为那不是你想要做的。希望这能回答你的问题。