Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pyramid: Check for permission given a route name

It is not hard to check for permission of a certain resource in Pyramid. I would like to know how to determine the permission type and the resource corresponding to a certain route.

Details. Assume that I have added a route

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

where FactoryFactory is a function returning a factory. In this case, it returns a factory for "resources", whatever that may be. Later then, I go like this:

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

At some point, I will generate an input element on the website which will link to

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

Edit: As requested, I will give you the exact template code hoping that this will give you a better understanding of the situation. The template engine we use is Chameleon.

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

Now what I would want is something like this:

    <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>

In other words: I want to display this input element only if the user has permission to perform the edit operation in the first place.


Now, I can give you an example of what would work but is unappealing to me because it is inflexible. I code a helper function

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

and then I write

    <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>

This is unappealing to me because the information about the factory and the information about the permission has been coded into the route, so it feels to me that I should be able to access it by simply providing the name of the route.


Therefore, I am looking for a way to obtain the resource and the permission from only the route information, i.e. from the string 'resource.edit' and the resource id some_input. Is this possible? If yes, how?

Thank you so much in advance.

like image 579
Jesko Hüttenhain Avatar asked Jan 04 '14 21:01

Jesko Hüttenhain


2 Answers

To your question:

Therefore, I am looking for a way to obtain the resource and the permission from only the route information, i.e. from the string 'resource.edit' and the resource id some_input. Is this possible? If yes, how?

Pyramid cannot know whether a user (current request) be able to access your resource before you provide one to it. You can reference to

Assigning ACLs to your Resource Objects

As you can see, Pyramid will look into __ACL__ attributes of your resource object (and __ACL__ of resource's ancestor as well), then determine who has what kind of permissions to this resource, therefore, it is not possible with Pyramid default authorization policy.

To solve your problem, you can either customize the AuthorizationPolicy, make your own authorization rule, or you can make creating the resource from ID easily.

Think about this, if you have an owner field of the corresponding table to the resource, and you need to check whether current user is the owner of specific resource, you will need to get that resource from database anyway. So you will need to create a resource which provides __ACL__ attributes anyway. So, my suggestion here is, make creating the resource much easier, make it something like this Resource(res.id), so that you can write your template like this

<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>

Update

Then I think what you need is to use introspector to get the factory and create the resource like this

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)
like image 182
Fang-Pen Lin Avatar answered Sep 23 '22 18:09

Fang-Pen Lin


If you want to extract the factory from route - use pyramid introspection

@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'])
    ...

Find more about introspection here.

like image 40
Sascha Gottfried Avatar answered Sep 23 '22 18:09

Sascha Gottfried