6

我正在尝试在我的网站中实现 Pyramid 的安全功能,但在弄清楚如何使用它时遇到了一些麻烦。

我一直在阅读本教程本示例以及 Pyramid 文档,但我不知道如何为单页 ID 实施授权策略。

例如,我有以下 URL 方案:

/pages
/pages/12

/pages显然列出了可用的页面,并且/pages/:id您可以在页面上阅读/评论。

我读过的文档/示例表明,您可以通过提供groupfinder带有组列表的回调来实现组级 ACS。如editoradmin等。

如何不使用组来获取权限,而是使用基于页面 ID 的权限?

在我上面的 URL 方案中,当用户浏览到时,/pages他们必须登录。当他们浏览到时/pages/:id,他们必须被授予查看该特定 ID 的访问权限。或者,他们必须是该页面的所有者。

与评论相同。在/page/:id页面上,他们可能已被授予查看页面的权限,但无法对其发表评论。

4

2 回答 2

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 类添加一个acl属性:

from pyramid import security

class Page(BaseObject):
    @property
    def __acl__(self):
        return [(security.Allow, self.userid, 'view')]

此 ACL 告诉金字塔只有 ID 存储在 page.userid 中的用户才能查看该页面。这里需要意识到的是,每个页面的 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_factory)没有找到匹配的页面,它将返回 None 而不是 Page 实例,因此金字塔不会使用此视图。结果金字塔会自动产生一个未找到的错误。

于 2012-04-22T15:49:13.227 回答
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”,并且 admin 组中的所有用户都可以访问您可能想要的视图permission='whatever'permission='whateverelse'并且不需要工厂,只需要一个返回当前用户的组列表的 groupfinder。唉,我离题了,因为这不是你想要做的。希望这能回答你的问题。

于 2012-04-22T14:48:16.797 回答