DRF ignoring permission classes for owner - django

I implemented BasePermission class in project but when I am going to retrieve logged user with his token it says You don't have a permission to perform this action
permissions.py
class IsLoggedInOrAdmin(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return obj.user == request.user or request.user.is_staff
class IsAdminUser(permissions.BasePermission):
def has_permission(self, request, view):
return request.user and request.user.is_staff
def has_object_permission(self, request, view, obj):
return request.user and request.user.is_staff
and my views file looks like this
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
def get_permissions(self):
permission_classes = []
if self.action == 'create':
permission_classes = [AllowAny]
elif self.action == 'retrieve' or self.action == 'update' or self.action == 'partial_update':
permission_classes = [IsLoggedInOrAdmin]
elif self.action == 'destroy' or self.action == 'list':
permission_classes = [IsAdminUser]
return [permission() for permission in permission_classes]
here what i have done so far. I created simple user and took token If I send GET request in Postman I am getting a detail error but it works fine with superuser's token but not owner. Where Am I making mistake? Any help please? Thanks in advance!

I am noting from DRF Official DOC
The instance-level has_object_permission method will only be called if
the view-level has_permission checks have already passed
Also note that in order for the instance-level checks to run, the
view code should explicitly call .check_object_permissions(request,
obj)
I think here you need explicitly called the check_object_permission not relaying on has_permission.

Related

Django rest framework custom permission for ViewSet

this is my modelViewSet
class UserViewSet(viewsets.ModelViewSet):
def list(self, request):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def create(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
pass
def retrieve(self, request, pk):
user = get_object_or_404(User, pk=pk)
serializer = UserSerializer(user)
return Response(serializer.data, status=status.HTTP_200_OK)
def get_permissions(self):
if self.action == "list":
permission_classes = [
IsAdminUser,
]
elif self.action == "create":
permission_classes = [AllowAny]
else:
permission_classes = [AccountOwnerPermission]
return [permission() for permission in permission_classes]
and this is the custom permission class
class AccountOwnerPermission(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
print(object)
print(request.user)
return obj == request.user
i access this view from another user and it show me user retrieve, and that 2 prints on
AccountOwnerPermission won't run. can someone tell me what is wrong with what i did and why has_object_permission wont run.
i change has_object_permission to has_permission and it works, but i dont have access to obj on the other hand
From the docs:
If you're writing your own views and want to enforce object level permissions, or if you override the get_object method on a generic view, then you'll need to explicitly call the .check_object_permissions(request, obj) method on the view at the point at which you've retrieved the object.
So you'll need to call check_object_permissions in your retrieve to be able to trigger has_object_permission:
def retrieve(self, request, pk):
user = get_object_or_404(User, pk=pk)
self.check_object_permissions(request, user) # Add this line
serializer = UserSerializer(user)
return Response(serializer.data, status=status.HTTP_200_OK)

has_object_permission not working for detail action decorator?

I have an private action decorator for a User View. I want the action to be accessible only for the User in question.
# views.py
class UserViewSet(viewsets.ModelViewSet):
queryset = get_user_model().objects.all()
serializer_class = UserSerializer
#action(detail=True, permission_classes=[IsSelf])
def private(self, request, pk):
user = get_object_or_404(get_user_model(), pk=pk)
data = UserPrivateSerializer(user).data
return Response(data, status=status=HTTP_200_OK)
# permissions.py
class IsSelf(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return obj == request.user
However, it looks like anyone can go to my private action - even if I explicitly declare IsSelf to be False:
class IsSelf(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
# This has no effect
return False
What am I missing?
FYI:
The instance-level has_object_permission(...) method will only be called if the view-level has_permission(...) checks have already passed. Since it is inherited from BasePermission, the has_permission(...) is already returning the True value.
The has_object_permission(...) method is getting called when you call the .get_object() method of the GenericAPIView.
class UserViewSet(viewsets.ModelViewSet):
queryset = get_user_model().objects.all()
serializer_class = UserSerializer
#action(detail=True, permission_classes=[IsSelf])
def private(self, request, *args, **kwargs):
user = self.get_object()
data = UserPrivateSerializer(user).data
return Response(data, status=status.HTTP_200_OK)

DRF Custom permissions for 1 view for auth and non auth users

Idea is non-auth user can only GET(list,retrieve) view. Auth user can GET,POST,PATCH,DELETE.
How can i do it in custom permission?
my view:
class BookViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated,)
serializer_class = serializers.BookSerializer
def get_queryset(self):
return Book.objects.filter(user=self.request.user)
You can do this by extending the base class permissions.BasePermission
from rest_framework import permissions
class IsAuthorOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.user == request.user
and then use that as a permission class permission_classes =(IsAuthorOrReadOnly,)

Django Rest Framework restrict user data view to admins & the very own user

I am using Django and DRF, and I would like to check if a user (regular one), after it has been authenticated, is allowed to view it's own profile and only that (no other user's).
serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('id', 'url', 'username', 'password', 'email', 'groups', 'is_staff')
def create(self, validated_data):
user = super().create(validated_data)
user.set_password(validated_data['password'])
user.save()
return user
Views.py
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer
permission_classes = (IsUser,)
permissions.py
class IsUser(permissions.BasePermission):
"""
Custom permission to only allow owners of an object to edit it.
"""
def has_permission(self, request, view, obj):
# View or Write permissions are only allowed to the owner of the snippet.
return obj.owner == request.user
This, obviously is not working, because is wrong. But I can not figure out how to allow a user to view:
http://127.0.0.1:8000/api/users/7
ONLY if its an admin, or the very same user doing the request.
And:
http://127.0.0.1:8000/api/users/
Only if it's an admin.
Thanks!
class UserViewSet(ModelViewSet):
queryset = Message.objects.all()
serializer_class = UserSerializer
def get_permissions(self):
if self.action == 'list':
self.permission_classes = [IsSuperUser, ]
elif self.action == 'retrieve':
self.permission_classes = [IsOwner]
return super(self.__class__, self).get_permissions()
class IsSuperUser(BasePermission):
def has_permission(self, request, view):
return request.user and request.user.is_superuser
class IsOwner(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.user:
if request.user.is_superuser:
return True
else:
return obj.owner == request.user
else:
return False
override list and retrieve method for UserViewSet probably the easiest way.
My apologies in advanced as I don't have enough rep to add to Ykh's answer
views.py
class UserViewSet(ModelViewSet):
queryset = Message.objects.all()
serializer_class = UserSerializer
def get_permissions(self):
# Overrides to tightest security: Only superuser can create, update, partial update, destroy, list
self.permission_classes = [IsSuperUser]
# Allow only by explicit exception
if self.action == 'retrieve':
self.permission_classes = [IsOwner]
return super().get_permissions()
permissions.py
from rest_framework.permissions import BasePermission
class IsSuperUser(BasePermission):
def has_permission(self, request, view):
return request.user and request.user.is_superuser
class IsOwner(BasePermission):
def has_object_permission(self, request, view, obj):
if request.user:
if request.user.is_superuser:
return True
else:
return obj.owner == request.user
else:
return False
Note: obj.owner otherwise you will always be denied if you are not a superuser.
Thank you Ykh for the base answer.
Add a additional check in IsUser permission .
if request.method == permissions.SAFE_METHOD:
return True
like sample, list for default-auth, edit only for superuser
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated, IsAdminUser
class CsrfExemptSessionAuthentication(SessionAuthentication):
def enforce_csrf(self, request):
return # To not perform the csrf check previously happening
class OnlyListAvaliableMixin:
authentication_classes = (CsrfExemptSessionAuthentication,)
def get_permissions(self):
permission_classes = [IsAuthenticated] if self.action == 'list' else [IsAdminUser] # noqa
return [permission() for permission in permission_classes]
# Example to use
class ListView(OnlyListAvaliableMixin, viewsets.ModelViewSet, ):
# premission_classes = (IsAdminUser,)
queryset = CarMark.objects.all()
serializer_class = ListViewSerializer

django rest framework - object permission does not trigger

I have the following setup:
permission.py
def get_permission_level(request, obj):
try:
level = PasswordListACL.objects.get(list=obj, user=request.user)
except PasswordListACL.DoesNotExist:
level = None
return level
class IsPasswordListOwner(permissions.DjangoObjectPermissions):
def has_permission(self, request, view):
return True
def has_object_permission(self, request, view, obj):
print(get_permission_level(request, obj))
if get_permission_level(request, obj) == None:
print('false')
return False
else:
level = AccessLevel.objects.get(pk=get_permission_level(request, obj).level_id).name
if level == 'Owner':
return True
else:
return False
I can confirm through my print('false') that the permission is being acted on and it is returning the correct value (I'm expecting False) however the view is still returning the data instead of a 403.
views.py
class PasswordListViewSet(viewsets.ModelViewSet):
queryset = PasswordList.objects.all()
serializer_class = PasswordListSerializer
permission_classes = (IsPasswordListOwner,)
def list(self, request):
self.permission_classes = [IsPasswordListOwner, ]
queryset = self.get_queryset().filter(passwordlistacl__user=request.user)
serializer = PasswordListSerializer(queryset, many=True, context={'request': request})
return Response(serializer.data)
def retrieve(self, request, pk=None):
self.permission_classes = [IsPasswordListOwner, ]
queryset = self.get_queryset()
if get_object_or_404(queryset, pk=pk):
password = get_object_or_404(queryset, pk=pk)
else:
pass
serializer = PasswordListSerializer(password, context={'request': request})
return Response(serializer.data)
edit - changing the return on has_permission confirms the permission there is overriding my has_object_permission?
Doing this gives me a 403 (correct):
def has_permission(self, request, view):
return False
The documentation states:
If you're writing your own views and want to enforce object level permissions, or if you override the get_object method on a generic view, then you'll need to explicitly call the .check_object_permissions(request, obj) method on the view at the point at which you've retrieved the object.
So since you're not calling get_object() you need to be calling self.check_object_permissions(self.request, obj) at some point.