Himanshu Poddar
Himanshu Poddar

Reputation: 7789

Django rest framework : Prevent one user from deleting/Editing/Viewing other users in ModelViewSet

I was using Django users model for my Django rest framework. For this I used Django's ModelViewSet for my User class.

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

Serializers.py

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'password']

        extra_kwargs = {
            'password' : {
                'write_only':True,
                'required': True
            }
        }
    
    def create(self, validated_data):
        user = User.objects.create_user(**validated_data)
        Token.objects.create(user=user) # create token for the user
        return user

But currently from postman when I make the request using the token of one user to view, delete, edit other users

http://127.0.0.1:8000/api/users/4/

Its able to edit/delete/view other users. I don't want that to happen and one user can make request on itself only is all I want.

This is my apps urls.py

urls.py

from django.urls import path, include
from .views import ArticleViewSet, UserViewSet
from rest_framework.routers import DefaultRouter


router = DefaultRouter()
router.register('articles', ArticleViewSet, basename='articles')
router.register('users', UserViewSet, basename = 'users')


urlpatterns = [
    path('api/', include(router.urls)), 
]

How can I prevent one user from accessing other users when they make GET/POST/PUT/DELETE request.

EDIT 1: After adding the IsOwnerOfObject class as provided in he answers below, now when I am requesting the detail of the user himself, I am getting

Authentication credentials were not provided.

enter image description here

Upvotes: 1

Views: 1712

Answers (4)

Grum
Grum

Reputation: 318

Another all-in-one solution could be to use a queryset filter to directly narrow the queryset results. This will prevent an user to delete other users, but also prevent any unauthorized retrieving or listing as the only accessible data will be the user itself only.

class UserViewSet(viewsets.ModelViewSet):

    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        """
        The get_queryset function is used to get the queryset of the user data corresponding to the logged in user.
        It is called when the view is instantiated, and it returns a list containing this user only.
        """
        # after get all products on DB it will be filtered by its owner and return the queryset
        owner_queryset = self.queryset.filter(username=self.request.user.username)
        return owner_queryset

And this can also be used with other objects to allow retrieving only data related to this user.

Upvotes: 0

Himanshu Poddar
Himanshu Poddar

Reputation: 7789

Building from Ene's answer, authentication and permission classes needs to be provided.

Create a file named permissions.py.

from rest_framework import permissions

class IsOwnerOfObject(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        return obj == request.user

next add the permission and authentication class to ModelViewSet:

from api.permissions import IsOwnerOfObject
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    permission_classes = [IsAuthenticated, IsOwnerOfObject]
    authentication_classes = (TokenAuthentication,)

Upvotes: 4

lucutzu33
lucutzu33

Reputation: 3700

Create a file named permissions.py.

from rest_framework import permissions


class IsOwnerOfObject(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):

        return obj == request.user

next add the permission class to you ModelViewSet:

from yourapp.permissions import IsOwnerOfObject

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    permission_classes = [IsOwnerOfObject, <other permission classes you want to use>]

More info here: https://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/#object-level-permissions

Upvotes: 3

Felix Ekl&#246;f
Felix Ekl&#246;f

Reputation: 3740

If you want to disable delete completely (Which is probably correct since if you want to "delete" a User you should deactivate it instead.) Then you can replace your view with this:

from rest_framework import viewsets
from rest_framework import generics

class UserViewSet(
    generics.CreateModelMixin,
    generics.ListModelMixin,
    generics.RetrieveModelMixin,
    generics.UpdateModelMixin,
    generics.viewsets.GenericViewSet
):
    queryset = User.objects.all()
    serializer_class = UserSerializer

And then you can use Ene Paul's answer to limit who can edit.

Upvotes: 2

Related Questions