-
-
Notifications
You must be signed in to change notification settings - Fork 7k
Closed
Description
Checklist
-
[ X] Raised initially as discussion #... In this case I am confident it is a bug (see below)
-
[ V] This cannot be dealt with as a third party library. (We prefer new functionality to be in the form of third party libraries where possible.)
-
[ V] I have reduced the issue to the simplest possible case.
Using:
Django==4.0.3
django-guardian==2.4.0
djangorestframework==3.13.1
djangorestframework-guardian==0.3.0
Create the following classes:
######## serializers.py
class MyUserSerializer(ModelSerializer):
class Meta:
model = User
fields = ["pk", "username", "email"]
######## views.py
class MyUserViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericViewSet):
permission_classes = [MyUserObjectPermissions]
serializer_class = MyUserSerializer
queryset = User.objects.all()
######### project urls.py
from accounts.api.views import MyUserViewSet
router = routers.DefaultRouter()
router.register('user', MyUserViewSet)
urlpatterns = [
path("api/", include(router.urls), name="api"),
]
Finally create the permissions class as well:
class MyUserObjectPermissions(DjangoObjectPermissions):
"""Checks if the user has object permissions, otherwise checks for model permissions"""
perms_map = {
"GET": ["%(app_label)s.view_%(model_name)s"],
"OPTIONS": ["%(app_label)s.view_%(model_name)s"],
"HEAD": ["%(app_label)s.view_%(model_name)s"],
"POST": ["%(app_label)s.add_%(model_name)s"],
"PUT": ["%(app_label)s.change_%(model_name)s"],
"PATCH": ["%(app_label)s.change_%(model_name)s"],
"DELETE": ["%(app_label)s.delete_%(model_name)s"],
}
def has_permission(self, request, view):
return True
# Raises the standard Django (not DRF) 404 page
# works when `?format=json` is appended at the end
def has_object_permission(self, request, view, obj):
print(f"Request method: {request.method}")
try:
has_perm = super().has_object_permission(request, view, obj)
print(f"Has obj perm: {has_perm}")
except (
PermissionDenied,
# has_object_permission() raises http.Http404 instead of
# drf_exceptions.NotFound when user does not have read permissions
# but this could change so check for both exceptions
Http404,
NotFound,
) as e:
print(f"Exception '{type(e)}' caught while checking for object perms" )
has_perm = super().has_permission(request, view)
print(f"Has model perm: {has_perm}")
# If user does not have model permissions, raise the original
# object permission error, which can be HTTP403 or HTTP404
if not has_perm:
raise e
return has_perm
The user I am testing with has:
- view, update, delete permissions for User with PK 3
- global view permissions for all objects in the user model (but is not a superuser or staff!)
Tests:
- access
api/user/3orapi/user/3?format=json:- Response: 200
- Console log:
Request method: GET
Has obj perm: True
Request method: PUT
Has obj perm: True
Request method: PATCH
Has obj perm: True
Request method: PUT
Has obj perm: True
Request method: DELETE
Has obj perm: True
Request method: OPTIONS
Has obj perm: True
- access
api/user/2(user with PK=2 exists!):- Response: 404, Django standard 404 page (missing template) and description of which URL path matched
- Console log:
Request method: GET
Exception '<class 'django.http.response.Http404'>' caught while checking for object perms
Has model perm: True
Request method: PUT
Exception '<class 'django.http.response.Http404'>' caught while checking for object perms
Has model perm: False
- access
api/user/2?format=json:- Response: 200
- Console log:
Request method: GET
Exception '<class 'django.http.response.Http404'>' caught while checking for object perms
Has model perm: True
Curiously enough, if I do not override the has_object_permission method - everything works as expected even though it still raises the same exception (Http404) as the logged in user does not have obejct permissions for user with pk == 2.
Metadata
Metadata
Assignees
Labels
No labels