没有遍历的Pyramid ACL

9 投票
1 回答
3370 浏览
提问于 2025-04-17 04:55

我对ACL(访问控制列表)是怎么工作的了解不多。我知道它很酷,能帮我省很多时间和麻烦。但现在我有点迷茫。所有关于金字塔框架的例子都使用了遍历(traversal)。而我只用URL调度(URL Dispatch)。我不太明白怎么构建一个资源树结构。

这里有一段代码示例:

class QuestionFactory(object):

    def __init__(self, request):
        self.__acl__ = default[:]
        self.uid = authenticated_userid(request)

        self.qid = request.matchdict.get('id')
        if self.qid:
            self.question = request.db.questions.find_one({'_id': ObjectId(self.qid)})
            if str(self.question.get('owner')) == self.uid:
                self.__acl__.append((Allow, userid, 'view'))     

问题是,这段代码能正常工作。但我必须为每种资源定义一个新的工厂(factory)。我不确定我该如何知道通过URL调度和工厂我想访问的是哪种资源。我想我应该看到这样的东西:

/accounts/{account}   //Owners only but viewable by anyone 
/messages/{message}   //Owners only
/configs/{config}     //Admin only
/pages/{page}         //Admins only but viewable by anyone

也就是说,我会有这样的结构:

  Root -\
         +-- account
         +-- message
         +-- config
         +-- page

这些工厂每个都有自己特殊的ACL。还有一点是,/accounts是主页面。它没有ID或其他东西。此外,/accounts/new也是一个特殊情况。它不是一个ID,而是创建新项目的视图。

我使用的是REST风格,包含GET/PUT/DELETE/POST请求。我不太确定我该如何自动将URL与资源和正确的ACL匹配。如果我在根目录定义一个像上面那样的特殊工厂,就没有问题。

编辑

我已经让它工作了,除了某些事情。我终于觉得我明白了遍历的目的。例如,我们有这个URL:/comments/9494f0eda/new,/comments/{comment}/new

我们可能在资源树中有两个节点,甚至三个节点。

RootFactory会首先被检查,然后根据我们的遍历,它会获取RootFactory的comments属性,然后是Comment工厂的“comment”,再然后是CommentFactory或对象本身的“new”。

我没有像Michael的例子那样使用工厂作为字典。

它看起来大致是这样的:

class RessourceFactory(object):
    def __init__(self, parent, name):

        self.__acl__ = []
        self.__name__ = name
        self.__parent__ = parent

        self.uid = parent.uid
        self.locale = parent.locale
        self.db = parent.db
        self.req = parent.req

这是我的基本资源对象。在每一步,它会从父级复制信息到新的子级。我当然可以将我的属性向上冒泡... context.parent._parent_.uid,但这样做并不是很好。

我不使用字典属性的原因是,我需要让它与

/comments

一起工作。出于某种原因,它确实创建了我的CommentFactory,但没有返回它,因为不需要一个键。

所以我的根工厂大致是这样的:

class RootFactory(object):

    def __init__(self, request):
        self.__acl__ = default[:]

        self.req = request
        self.db = request.db

        self.uid = authenticated_userid(request)
        self.locale = request.params.get('locale', 'en')

    def __getitem__(self, key):

        if key == 'questions':
            return QuestionFactory(self, 'questions')
        elif key == 'pages':
            return PageFactory(self, 'pages')
        elif key == 'configs':
            return ConfigFactory(self, 'configs')
        elif key == 'accounts':
            return AccountFactory(self, 'accounts')

        return self

如果没有找到项目,RootFactory会返回自身;如果找到了,就返回一个新的工厂。因为我的代码是基于Michael的代码,所以工厂构造函数有第二个参数。我不确定是否要保留它,因为QuestionFactory已经知道如何处理“问题”,所以这里不需要命名工厂。它应该已经知道自己的名字。

class QuestionFactory(RessourceFactory):
    def __init__(self, parent, name):
        RessourceFactory.__init__(self, parent, name)
        self.__acl__.append((Allow, 'g:admin', 'view'))
        self.__acl__.append((Allow, 'g:admin', 'edit'))
        self.__acl__.append((Allow, 'g:admin', 'create'))
        self.__acl__.append((Allow, 'g:admin', 'delete'))
        self.__acl__.append((Allow, Everyone, 'create'))

    def __getitem__(self, key):

        if key=='read':
            return self

        self.qid = key
        self.question = self.db.questions.find_one({'_id': ObjectId(self.qid)})

        if str(self.question.get('owner')) == self.uid:
            log.info('Allowd user %s' % self.uid)
            self.__acl__.append((Allow, self.uid, 'view'))
            self.__acl__.append((Allow, self.uid, 'edit'))
            self.__acl__.append((Allow, self.uid, 'delete'))

        return self

所以几乎所有的逻辑都会在这里进行。在init中,我设置了适用于/questions的ACL,在getitem中,它会适用于/questions/{id}/*

因为我返回自身,任何经过这个资源工厂的getitem都会指向自身,除非我为某些特殊情况返回一个新的工厂。这样做的原因是,我的上下文不仅仅是数据库中的一个对象或一个对象。

我的上下文处理多个事情,比如用户ID、地区等... 当ACL设置完成后,我就有了一个新的上下文对象可以使用。这减少了视图中的大部分逻辑。

我可能可以设置事件来查询地区和用户ID,但在这里并不合适。如果我需要任何新东西,我只需编辑我的RootFactory和资源工厂,将它们复制到子工厂。

这样,如果某些东西需要在所有视图中更改,就没有任何冗余。

1 个回答

6

看起来你对一些对象/行级安全功能感兴趣,这样只有账户的拥有者才能查看他们的数据。我建议你看看我之前在StackOverflow上关于这个话题的回答,还有我正在做的一个关于URL调度的认证教程,这个教程是围绕那个回答展开的。特别是你可能想看看链接的GitHub项目中的2.object_security演示,以及我网站上渲染的HTML中解释资源树的文档。

Pyramid存储项目的授权

https://github.com/mmerickel/pyramid_auth_demo

http://michael.merickel.org/projects/pyramid_auth_demo/

如果你对理解这些资源有任何问题,我很乐意在这里进一步解释。

撰写回答