I use a route factory and inside that factory if I can't resolve the
context then I raise HTTPNotFound.  The factories let you use context
without using traversal.

Something like this:

def member_factory(request):
    member = lookup_member(request.matchdict['memberid'])
    if not member:
        raise HTTPNotFound()
    return member

config.add_route('members-api', '/api/groups/{groupid}/members/{memberid}',
factory=member_factory)

Furthermore you can also check if other path parts don't make sense and
404, ie.  member.group.id != request.matchdict['groupid'].  Finally if you
have an id and a slug, for something like,
/articles/{article_id}--{article_slug}, you can redirect to the latest
slug, raising HTTPMovedPermanently, but still keep url integrity by using a
stable id. It works pretty well.



On Thu, Nov 21, 2024 at 8:43 AM Sean Hammond <
[email protected]> wrote:

> Hey,
>
> I'm wondering what's the best way for a Pyramid app that uses URL dispatch
> rather than traversal to handle requests that reference resources that
> don't exist.
>
> For example imagine a JSON API request like this, to edit the role of a
> member in a group:
>
>     HTTP PATCH /api/groups/{groupid}/members/{memberid}
>
> (Where the JSON body of the request would be something like this:
> `{"role": "admin"}`.)
>
> And imagine someone makes a request with a {groupid} and/or {memberid}
> that doesn't exist:
>
>     HTTP PATCH /api/groups/UNKNOWN_GROUPID/members/UNKNOWN_MEMBERID
>
> And imagine that this request routes to a view callable with a config like
> this:
>
>     @view_config(
>         route_name="whatever",
>         request_method="PATCH",
>         permission="edit",
>     )
>     def edit_membership(context, request):
>         ...
>
> Because of the `permission` when Pyramid handles the request it's going to
> call the permits() method of the app's security policy with the request,
> context and "edit" for the permission. This is where I get confused:
> permits() must return either Allowed or Denied, and neither seems correct:
>
> 1. It doesn't seem quite right to me for permits() to return Denied in
> this situation. It's not that the request doesn't have permission to edit
> the membership, the problem is that the membership doesn't exist. Returning
> Denied would cause Pyramid to send a 401 response, when a 404 would be more
> appropriate.
>
> 2. permits() could return Allowed and then the view could raise
> HTTPNotFound. This would return the correct response but it seems wrong to
> me for permits() to say that the request is permitted to edit a membership
> that doesn't exist and I imagine there could even be race conditions
> resulting in security issues (if a membership with the matching IDs gets
> created by a concurrent request and it happens to be a membership that the
> API request should *not* have been permitted to edit).
>
> (1) might be acceptable if you're happy for requests to unknown resources
> to 401 rather than 404ing. Is that the answer?
>
> Otherwise I don't see an acceptable option. Am I missing something?
>
> If using either traversal or hybrid traversal + URL dispatch (
> https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/hybrid.html)
> there is a solution: if the groupid and/or memberid doesn't exist then the
> app's resource lookup code can raise KeyError which will cause Pyramid to
> 404 without ever calling the security policy. The security policy code can
> be simpler because it can assume that if it is called then the groupid and
> memberid must have already been found by resource lookup. And the view code
> can be simpler because it can assume that if it is called then the groupid
> and memberid have been found and the permissions check has passed. Perfect!
>
> But how should a URL dispatch-based app that doesn't use traversal handle
> this situation? Or do you just have to use traversal?
>
> Thanks!
>
> --
> You received this message because you are subscribed to the Google Groups
> "pylons-discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To view this discussion visit
> https://groups.google.com/d/msgid/pylons-discuss/d9337096-7d9d-461b-8972-391b0a62fe69%40app.fastmail.com
> .
>

-- 
You received this message because you are subscribed to the Google Groups 
"pylons-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/pylons-discuss/CADj1%3D65cvgxewqQmDJ4i8t-3UBQ-cB8pHH%2BhXgHVxb719A0V0g%40mail.gmail.com.

Reply via email to