-
Notifications
You must be signed in to change notification settings - Fork 149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use APIView.permission_denied to handle 401 and 403 http errors #175
base: master
Are you sure you want to change the base?
Conversation
@@ -72,4 +72,4 @@ def initial(self, *args, **kwargs): | |||
# Finally, check permission | |||
perm = self.get_queryset().model.get_perm(perm_type) | |||
if not self.request.user.has_perm(perm, obj): | |||
raise PermissionDenied | |||
self.permission_denied(self.request) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is permission_denied
an attribute of DRF's ViewSet
? Is this possible to result in an AttributeError
?
Also, what are the implications of switching to the new behavior for users that depend on PermissionDenied
be raised and is there a way to get the previous behavior?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is permission_denied an attribute of DRF's ViewSet? Is this possible to result in an AttributeError?
permission_denied
is defined in APIView
, and since ViewSet
is a subclass of APIView
, the function should always works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, what are the implications of switching to the new behavior for users that depend on PermissionDenied be raised ...?
The view will return the status code correctly based on whether the request is authenticated (the credentials are valid, but the user doesn't have the correct permission) or not (the credentials is not provided or invalid).
Quoting from DRF docs:
HTTP 401 responses must always include a WWW-Authenticate header, that instructs the client how to authenticate. HTTP 403 responses do not include the WWW-Authenticate header.
The kind of response that will be used depends on the authentication scheme. Although multiple authentication schemes may be in use, only one scheme may be used to determine the type of response. The first authentication class set on the view is used when determining the type of response.
Note that when a request may successfully authenticate, but still be denied permission to perform the request, in which case a 403 Permission Denied response will always be used, regardless of the authentication scheme.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... is there a way to get the previous behavior?
For current implementation, the users needs to override the initial()
method and replace the NotAuthenticated
exception with PermissionDenied
(using try except block).
As an alternative implementation, we could allow users to opt in/out of this change using a Django setting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The view will return the status code correctly based on whether the request is authenticated (the credentials are valid, but the user doesn't have the correct permission) or not (the credentials is not provided or invalid).
I understand that, what I mean is that this is changing a behavior that existing users may (?) depend on in a way that might cause crashes at runtime so I'm trying to understand the (potential) impact this change may have.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I could only think of a mismatch of HTTP status code checks (most likely on client side) led to unhandled (or incorrect) condition?
Something like this:
if status_code == 200:
...
elif status_code == 404:
...
elif status_code == 403:
...
else:
return "Unhandled message"
I don't think that would lead to a crash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @dfunckt, would you prefer to let the user opt-in for this patch for now? I was thinking that you might consider this a breaking change, so enforcing it on the next major release might be more suitable. But still, I think this is a bug, so it should be applied with a patch release (or at least a minor release).
Reference: https://www.django-rest-framework.org/api-guide/authentication/#unauthorized-and-forbidden-responses
I change the test viewset to use BasicAuthentication because the DRF default will always return 403 since the first auth class is SessionAuthentication.