Skip to content
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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

hashlash
Copy link
Contributor

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.

@@ -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)
Copy link
Owner

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?

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Owner

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants