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!