【问题标题】:Pyramid: Check for permission given a route namePyramid:检查给定路线名称的权限
【发布时间】:2014-01-22 11:39:06
【问题描述】:

not hard to check Pyramid 中某个资源的权限。我想知道如何确定某个路由对应的权限类型和资源。

详细信息。假设我添加了一条路线

config.add_route('resource.edit', '/t/{resource_id:\d+}/edit',
    factory = FactoryFactory('resource') ))

FactoryFactory 是一个返回工厂的函数。在这种情况下,它会为 "resources" 返回一个工厂,不管它是什么。后来,我是这样去的:

@view_config(
    route_name='resource.edit',
    renderer='resource-edit.pt',
    permission='edit'
)
def edit(resource_object, request):
    ...

在某个时候,我将在网站上生成一个输入元素,该元素将链接到

request.route_url('resource.edit', resource_id=some_input)

编辑:根据要求,我会给你确切的模板代码,希望这能让你更好地了解情况。我们使用的模板引擎是 Chameleon。

    <a href="${request.route_url('resource.edit', resource_id=res.id)}" 
       class="btn btn-info morespacing"><i class="icon-edit"></i>edit</a>

现在我想要的是这样的:

    <a tal:condition="h.route_accessible(request, 'resource.edit', res.id)" 
       href="${request.route_url('resource.edit', resource_id=res.id)}" 
       class="btn btn-info morespacing"><i class="icon-edit"></i>edit</a>

换句话说:我想仅在用户有权执行编辑操作时才显示此输入元素


现在,我可以给你举个例子,说明什么可行但对我没有吸引力,因为它不灵活。我编写了一个辅助函数

def can_edit(request, factory_type, res_id):
    resource = FactoryFactory(factory_type)(res_id)
    return has_permission('edit', resource, request)

然后我写

    <a tal:condition="h.can_edit(request, 'resource', res.id)" 
       href="${request.route_url('resource.edit', resource_id=res.id)}" 
       class="btn btn-info morespacing"><i class="icon-edit"></i>edit</a>

这对我没有吸引力,因为有关工厂的信息有关权限的信息已被编码到路由中,所以我觉得我应该能够通过简单地提供来访问它路线的名称


因此,我正在寻找一种从路由信息(即字符串'resource.edit' 和资源ID some_input)获取资源和权限的方法。这可能吗?如果是,怎么做?

非常感谢您。

【问题讨论】:

  • 我多次阅读你的问题,我不明白你的意思。很可能您正在寻找常见问题的解决方案。您要求提供实现细节,但您应该使用 ACL 解释您的资源模型、具有权限的用户,并在某些 URL 上演示您的问题以说明上下文。
  • 真的没什么好说的了......我想要一个函数,它将路由名称(在上述情况下为'resource.edit')、资源ID和请求对象作为输入如果当前用户有权访问该路由,则返回True,否则返回False
  • 现在,您有 2 个答案。两者似乎都对您没有太大帮助。可能意味着您没有很好地解释您的问题。添加一些视图代码和/或模板代码来显示您的问题。您想为每个小辅助函数开始赏金,还是获得一些建议来选择适合您的问题领域的金字塔概念?
  • 为了您的方便,我将添加一些模板代码。但是,请不要光顾。我有一个非常具体的问题,它与适合我的问题领域的金字塔概念无关。这是一个关于在给定已通过view_config 设置的路由名称的情况下确定访问权限的技术问题。

标签: python python-2.7 permissions pyramid


【解决方案1】:

你的问题:

因此,我正在寻找一种方法来获取资源和 仅来自路线信息的许可,即来自字符串 'resource.edit' 和资源 ID some_input。这可能吗?如果 是的,怎么做?

在您向其提供资源之前,Pyramid 无法知道用户(当前请求)是否能够访问您的资源。可以参考

Assigning ACLs to your Resource Objects

如您所见,Pyramid 会查看您的资源对象的 __ACL__ 属性(以及资源​​祖先的 __ACL__ 属性),然后确定谁对该资源拥有什么样的权限,因此,Pyramid 默认授权是不可能的政策。

为了解决您的问题,您可以自定义AuthorizationPolicy,制定自己的授权规则,或者您可以轻松地从ID创建资源。

考虑一下,如果你有一个资源对应表的owner字段,并且你需要检查当前用户是否是特定资源的所有者,那么无论如何你都需要从数据库中获取该资源。因此,无论如何您都需要创建一个提供 __ACL__ 属性的资源。所以,我的建议是,让创建资源变得更容易,让它像Resource(res.id)这样,这样你就可以像这样编写你的模板

<a tal:condition="has_permission('edit', Resource(res.id), request)" 
   href="${request.route_url('resource.edit', resource_id=res.id)}" 
   class="btn btn-info morespacing"><i class="icon-edit"></i>edit</a>

更新

那么我认为你需要的是使用内省来获取工厂并创建这样的资源

def has_permission_for_route(request, route_name, permission, res_id):
    introspector = request.registry.introspector
    route_name = request.matched_route.name
    route_intr = introspector.get('routes', route_name)
    factory = route_intr['factory']

    resource = factory(request, res_id=res_id)
    return has_permission(permission, resource, request)

【讨论】:

    【解决方案2】:

    如果你想从路由中提取工厂 - 使用金字塔内省

    @view_config(route_name='bar')
    def show_current_route_pattern(request):
        introspector = request.registry.introspector
        route_name = request.matched_route.name
        route_intr = introspector.get('routes', route_name)
        factory = route_intr['factory'])
        ...
    

    了解更多关于内省的信息here

    【讨论】:

    • 这看起来很有希望!谢谢一堆=)。我会接受 Victor Lin 的回答,因为它更详细,但我会将赏金奖励给你,因为你先发布了关于自省者的信息。
    • 很高兴你注意到了这一点。
    • 是的,对不起,我更新了我的答案,然后注意到你已经发布了这个
    【解决方案3】:

    回答您的问题:不,您不会从路线获得有关许可的信息。路线只匹配视图。可以使用许多不同的方式使用权限来保护视图和资源。您的安全策略通常与用户、资源和视图相关 - 而不是路由。

    也许这个建议对你有帮助。

    给定到资源的这条路线

    config.add_route('resource.view', '/t/{resource_id:\d+}',
        factory = FactoryFactory('resource') ))
    

    你创建一个视图来匹配这条路线。路由工厂将资源作为上下文制造。 在视图中封装权限相关代码

    @view_config(
        route_name='resource.view',
        renderer='resource-view.pt',
        permission='view'
    )
    def view_page(resource_object, request):
        can_edit = has_permission('edit', resource_object, request)
    
        #do some more stuff - extend the dictionary passed to the renderer
        ...
    
        return dict(
            res = resource_object,
            can_edit = can_edit,
        )
    

    在您的模板中,您使用“can_edit”来控制此链接或任何其他标记的可见性。

    <a tal:condition="can_edit"
       href="${request.route_url('resource.edit', resource_id=res.id)}" 
       class="btn btn-info morespacing"><i class="icon-edit"></i>edit</a>
    

    您也可以通过计算 URL 来在视图代码中编辑资源来改进示例。

    @view_config(
        route_name='resource.view',
        renderer='resource-view.pt',
        permission='view'
    )
    def view_page(resource_object, request):
        can_edit = has_permission('edit', resource_object, request)
        edit_url = request.route_url('resource.edit', resource_id=resource_object.id)
    
        #do some more stuff - extend the dictionary passed to the renderer
        ...
    
        return dict(
            can_edit = can_edit,
            edit_url = edit_url,
        )
    

    这会导致模板代码紧凑并提高视图代码的可测试性。

    <a tal:condition="can_edit" 
       href="${edit_url}" 
       class="btn btn-info morespacing"><i class="icon-edit"></i>edit</a>
    

    为了使代码对任何金字塔开发人员更通用,我想使用框架选项从请求中获取上下文。

    @view_config(
        route_name='resource.view',
        renderer='resource-view.pt',
        permission='view'
    )
    def view_page(request):
        context = request.context
        can_edit = has_permission('edit', context, request)
        edit_url = request.route_url('resource.edit', resource_id=context.id)
    
        #do some more stuff - extend the dictionary passed to the renderer
        ...
    
        return dict(
            can_edit = can_edit,
            edit_url = edit_url,
        )
    

    关于您的应用,我想推荐这些金字塔示例应用:

    【讨论】:

      【解决方案4】:

      你有没有用过...

      http://ziggurat-foundations.readthedocs.org/en/latest/

      在此您可以轻松地测试特定用户权限,因此您可以在模板或代码中轻松过滤用户权限。

      t = session.query(UserPermission).\
          filter(UserPermission.user_id == 'userid').\
          filter(UserPermission.perm_name == 'yourpermission')).first()
      

      然后在模板中(显示的 Jinja2 示例)你可以这样做:

      {% if t %}
          <!-- show edit link -->
      {% endif %}
      

      或者,如果您正在执行用户查询,您可以将连接与当前查询联系起来。

      【讨论】:

      • 感谢您的提示,这看起来确实很有趣:但是,它并没有真正解决问题,因为我仍然需要在两个地方指定权限,一次在 view_config 中,一次在定义t。用户是否有权访问某个链接仅取决于他的 ID 和路线名称,因此应该可以从该数据中以某种方式提取该信息。
      【解决方案5】:

      您可以将权限存储在资源上(持久或仅作为类的属性):

      def dynamic_resource_permission(context, request):
          permission = getattr(context, 'view_permission', 'view')
          if not has_permission(context, permission):
              raise HTTPForbidden()
          return True
      
      @view_config(
        route_name='resource.edit',
        renderer='resource-edit.pt',
        permission='edit',
        custom_predicates=(dynamic_resource_permission,),
      )
      def edit(resource_object, request):
          ....
      

      PS:我还没有运行上面的,但上面的一些变体应该可以工作。

      【讨论】:

      • 这将如何帮助我不显示到禁止路线的链接?事实上,这有什么改变呢?当有人未​​经许可尝试解析该路由时,无论如何都会引发 HTTPForbidden。
      猜你喜欢
      • 1970-01-01
      • 2015-07-25
      • 2021-04-02
      • 2013-04-27
      • 2015-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-20
      相关资源
      最近更新 更多