I am trying to create a custom permission for my view that allow read and write permissions to the owner of the model in the QuerySet but do not allow any permission/request to other users or un-authenticated ones.
Source: https://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/
View:
class My_classListCreateAPIView(generics.ListCreateAPIView):
queryset = Model.objects.all()
serializer_class = ModelSerializer
permission_classes = [IsModelOwner]
Permission:
class IsModelOwner(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
# Permissions are only allowed to the owner of the model and admins.
if request.user.is_staff == True:
return True
return obj.owner == request.user
unfortunately it seems that my view is not even calling my custom permission class. (I imported it etc.) If instead of my custom permission class, I use a default one like permissions.isAuthenticatedOrReadOnly that works instead. What am I missing here?
Thanks.
The has_object_permission method is only called on objects, not on querysets, what this means is that on a list request it won't be called.
Your view only has list and create endpoints, neither of those use the object_permissions, use has_permission instead.
However i believe what you want to do is actually use the isAuthenticated permission combined with a modified get_queryset in your view
class My_classListCreateAPIView(generics.ListCreateAPIView):
queryset = Model.objects.all()
serializer_class = ModelSerializer
permission_classes = [isAuthenticated]
def get_queryset(self):
return Model.objects.filter(owner=self.request.user)
Related
Viewsets are convenient because we can do stuff like this and get a fully working serializer:
class StoreObjectViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
permission_classes = [IsAuthenticated]
queryset = StoreObject.active_objects.all()
serializer_class = serializers.StoreObjectSerializer
Unfortunately, as far as I know– to pass the context into the serializer we need to do things like this:
PostSerializer(data=request.data, context={'request': request})
Which means we need to manually override every convenient method provided by ViewSets (as far as I know). Is there a way to inject the context into every serializer while still keeping Viewsets convenient?
By default, request is being sent to any Generic View and ViewSet. You can check the source code in GitHub as well. So you do not have to inject them in every view. If you want to pass extra context, then override get_serializer_context(...) method:
class StoreObjectViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
...
def get_serializer_context(self):
context = super().get_serializer_context()
context['custom_context'] = 'Your custom context'
return context
I use Django group permissions, I can assign some permission for users.
Here is my test scenario:
I have a Company Model,
User1.hasPerm -> view_company, change_company, add_company, delete_company
Permissions.py:
class HasPermissions(permissions.BasePermission):
def has_permission(self, request, view):
if request.user.has_perm('MYAPP.view_company'):
return True
else:
return False
if request.user.has_perm('MYAPP.change_company'):
return True
else:
return False
if request.user.has_perm('MYAPP.add_company'):
return True
else:
return False
if request.user.has_perm('MYAPP.delete_company'):
return True
else:
return False
return True
CompanyView.py:
class CompanyViewSet(ModelViewSet):
queryset = Company.objects.all()
filter_class = CompanyFilter
serializer_class = CompanySerializer
permission_classes = [IsAuthenticated, HasPermissions]
def get_queryset(self):
if self.request.user.is_authenticated and self.request.user.is_active:
company = Company.objects.filter(companyUser__exact=self.request.user)
return company
else:
return Company.objects.all()
I wrote HasPermissions function to control user permissions, and this function works only CompanyView. I want to make global this function for I can control all view.
HasPermissions function is like hard coding, I want to change the more usable version to control all views.
How can ı do this?
You do not need to write this yourself. Django already has a DjangoModelPermissions [drf-doc] for that:
This permission class ties into Django's standard django.contrib.auth
model permissions. This permission must only be applied to views that
have a .queryset property set. Authorization will only be granted if
the user is authenticated and has the relevant model permissions
assigned.
POST requests require the user to have the add permission on the
model.
PUT and PATCH requests require the user to have the change permission on the model.
DELETE requests require the user to have the delete permission on the model.
So you can use:
class CompanyViewSet(ModelViewSet):
queryset = Company.objects.all()
filter_class = CompanyFilter
serializer_class = CompanySerializer
permission_classes = [IsAuthenticated, DjangoModelPermissions]
I'm reading http://www.django-rest-framework.org/api-guide/permissions/ and trying to relate it to the OAuth2 toolkit documentation, http://django-oauth-toolkit.readthedocs.io/en/latest/rest-framework/getting_started.html. The latter has an example in which in settings.py one specifies
REST_FRAMEWORK = {
# ...
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)
}
and in addition, IsAuthenticated is also specified added to the permission_classes list of a ModelViewSet:
class UserViewSet(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
queryset = User.objects.all()
serializer_class = UserSerializer
Do I infer correctly from this example that the DEFAULT_PERMISSION_CLASSES are not prepended / postpended to a ModelViewSet's permission classes, but are instead replaced by it?
In the Django REST framework, how are the default permission classes combined with per-view(set) ones?
They are not combined.
... the DEFAULT_PERMISSION_CLASSES are not prepended / postpended to a ModelViewSet's permission classes, but are instead replaced by it?
Correct.
Do I infer correctly from this example that the
DEFAULT_PERMISSION_CLASSES are not prepended / postpended to a
ModelViewSet's permission classes, but are instead replaced by it?
The DEFAULT_PERMISSION_CLASSES are used for views/viewsets where permission_classes is not defined. In the cases they are defined, those are used instead, not the default ones.
If you do want to extend the default permissions, this seems to work.
Disclaimer: I found it by looking into DRF's code, not sure it is documented.
from rest_framework.settings import api_settings
class UserViewSet(viewsets.ModelViewSet):
permission_classes = [*api_settings.DEFAULT_PERMISSION_CLASSES, TokenHasReadWriteScope]
Add code in your custom Permission class like this
class ObjectWritePermission(BasePermission):
# you will see this function in IsAuthenticated Permission class
def has_permission(self, request, view):
return bool(request.user and request.user.is_authenticated)
def has_object_permission(self, request, view, obj):
return obj.user == request.user
Trying to manage django-guardian object-level permissions with django-rest-framework the most cleanly and canon possible.
I want to assign a read permission (module.view_object) of an object to to the user making the request when performing a POST.
My class-based view:
class ObjectList(ListCreateAPIView):
queryset = Object.objects.all()
serializer_class = ObjectSerializer
filter_backends = (DjangoObjectPermissionsFilter,)
# MyObjectPermissions inherit from DjangoObjectPermissions and just
# add the 'view_model' permission
permission_classes = (MyObjectPermissions,)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
As described in the django-rest-framework documentation, I've overloaded perform_create in order to pass the user to the serializer.
But how do I assign the permission (using guardian.shortcuts.assign_perm) in the serializer. Is there no other way but to override the save method to assign the permission manually? Isn't there some kind of standard mechanism to manage a common behavior as this one?
You can assign the permission in the perform_create() method of the ViewSet.
I am new to DRF, and I had came across the following problem when I trying to customize the Permission in DRF.
Suppose I had the following code in my permissions.py file:
class GetPermission(BasePermission):
obj_attr = 'POITS'
def has_permission(self, request, view):
user = request.user
employee = Employee.objects.get(user=user)
# Return a dict which indicates whether the request user has the corresponding permissions
permissions = get_permission(employee.id)
return permissions[GetPermission.obj_attr]
And in my view, I want to override the static variable in the GetPermission class:
class AssignmentList(generics.ListCreateAPIView):
GetPermission.obj_attr = 'ASSIGNMENT'
permission_classes = (IsAuthenticated, IsStaff, GetPermission)
queryset = Assignment.objects.all()
serializer_class = AssignmentSerializer
pagination_class = LargeResultsSetPagination
def perform_create(self, serializer):
employee = Employee.objects.get(user=self.request.user)
serializer.save(sender=employee, status=0, operatable=0)
However, as the documentation of DRF points out:
Permission checks are always run at the very start of the view, before any other code is allowed to proceed.
So how am I supposed to do, thanks in advance, any ideas will be welcomed, since I am a new fish to DRF.
You need to create subclasses of your permission for each attribute, and use self.obj_attr inside the has_permission method.
class DefaultPermission(GetPermission):
obj_attr = 'POITS'
class AssignmentPermission(GetPermission):
obj_attr = 'ASSIGNMENT'