I have following functions in rest API for User model. I want to set AllowAny permission on only POST request. Can someone help me out.
class UserList(APIView):
"""Get and post users data."""
def get(self, request, format=None):
"""Get users."""
users = User.objects.all()
serialized_users = UserSerializer(users, many=True)
return Response(serialized_users.data)
def post(self, request, format=None):
"""Post users."""
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
You can write a custom Permission class IsPostOrIsAuthenticated which will allow unrestricted access to POST requests but will allow only authenticated GET requests.
To implement the custom permission IsPostOrIsAuthenticated, override the BasePermission class and implement .has_permission(self, request, view) method. The method should return True if the request should be granted access, and False otherwise.
from rest_framework import permissions
class IsPostOrIsAuthenticated(permissions.BasePermission):
def has_permission(self, request, view):
# allow all POST requests
if request.method == 'POST':
return True
# Otherwise, only allow authenticated requests
# Post Django 1.10, 'is_authenticated' is a read-only attribute
return request.user and request.user.is_authenticated
So, all POST requests will be granted unrestricted access. For other requests, authentication will be required.
Now, you need to include this custom permission class in your global settings.
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'my_app.permissions.IsPostOrIsAuthenticated',
)
}
http://www.django-rest-framework.org/api-guide/permissions/
as per above URL you have to write one custom permission class
class ExampleView(APIView):
permission_classes = (MyCustomAuthenticated,)
Write your own logic using AllowAny or IsAuthenticated inside MyCUstomAuthenticated based on POST and GET
Related
I am using DRF with authentication. I have implemented both Session and Token auth. The first is used by the browsable API that I use to debug and the second is the auth that will be used by the actual client. I have the following detail view:
#api_view(['GET', 'PUT', 'DELETE'])
#permission_classes([permissions.IsAuthenticated, IsTrainingOwner]) # this view is only available to authenticated users
def training_detail(request, pk, format=None):
"""
Retrieve, update or delete a training. Need to be authenticated
"""
print(request.user)
try:
training = Training.objects.get(pk=pk)
except Training.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = TrainingSerializer(training)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = TrainingSerializer(training, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
training.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
As you cas see I am using a custom permission:
class IsTrainingOwner(permissions.BasePermission):
"""
Custom permission to only allow owners of an object to see and edit it
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
print("Object owner: ", obj.owner)
print("Request user: ", request.user)
return True
# Instance must have an attribute named `owner`.
print("Object owner: ", obj.owner)
print("Request user: ", request.user)
return obj.owner == request.user
This custom permission is being correctly called when I use the DRF browsable API, with the prints of object owner and request user being displayed in my console.
However, when I use Postman to test the Token permission, the 'has_object_permission' method is not called at all (I don't see any prints in my console) and I can modify the training of a user A when sending the token of a user B in the header.
I have read the DRF doc that talks about this and then decided to try a generic view:
class TrainingDetail(generics.RetrieveUpdateAPIView):
permission_classes = [permissions.IsAuthenticated, IsTrainingOwner]
queryset = Training.objects.all()
serializer_class = TrainingSerializer
Using the generic view works well and the custom permission is invoked correctly.
So, my question is:
Why does the custom permission works with the function based view version when I use the browsable API which uses session auth and I need to switch to the generic view version for the custom permission to work with POSTMAN?
Thank you for your help :)
Have a look at the DRF documentation:
https://www.django-rest-framework.org/api-guide/permissions/#object-level-permissions
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.
I think that in case of interacting with the API via the HTML interface, DRF is calling check_object_permissions while rendering the HTML form while your view isn't touching it at all.
Please try pulling the session cookie via browser dev tools and try using POSTMAN/curl to send the same request to fire JSONRenderer rather than BrowsableAPIRenderer (you can also force that by adding ?format=json in the url).
I have used ListCreateAPIView and RetrieveUpdateDestroyAPIView for a model. Now I want to add JWT authentication to only the Update and Destroy part in the RetrieveUpdateDestroyAPIView. How can I do that?
Let me make my question a bit more clear. I have a model named Post. Now All users are allowed to view the post but update, delete is only available to the user who created it. And I want to use JWT Authentication.
You can write custom permission class for this:
from rest_framework import permissions
class CustomPermission(permissions.BasePermission):
def has_permission(self, request, view):
if view.action in ('update', 'destroy'):
return request.user.is_authenticated
return True
And use in in your view:
class ExampleView(RetrieveUpdateDestroyAPIView):
permission_classes = (CustomPermission,)
we can override the method get_authenticators and don't forget to add authentication_classes to api view.
def get_authenticators(self):
if self.request.method in ['PUT', 'DELETE']:
return [auth() for auth in self.authentication_classes]
else:
return []
For your question update we need to add object level permissions like below
class OwnerRequiredPermission(object):
def has_object_permission(self, request, obj):
return obj.created_by == request.user
add above permission class to permission_classes
I am building a Restapi using Django and Rest framework and mongoengine, so far all requests require a user to be authenticated and check against a token.
But now I need to allow different actions to different users. I don't know where to begin. Any guidelines ?
For example I want only the admin to be able to write and read users objects:
class UsersViewSet(ModelViewSet):
queryset = Users.objects.all()
serializer_class = UsersSerializer
def me(self, request, *args, **kwargs):
serializer = self.serializer_class(request.user)
return Response(serializer.data)
Read the chapter on custom permisssion. You will want to extend permissions.BasePermission and provide the authentication logic inside has_permission.
from rest_framework import permissions
class CustomUserPermission(permissions.BasePermission):
def has_permission(self, request, view):
# return True if user has permission
pass
Then inside your view.
class UsersViewSet(ModelViewSet):
permission_classes = (CustomUserPermission,)
I have a Django Rest Framework application.
Authentication is performed through a login method:
def login(self, request):
user = find_my_user(request)
user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request, user)
return Response({"status": "ok"})
Authentication works fin.
I have a ViewSet having a list_route() that need authenticated user to be used.
Here is the code:
class CommonView(viewsets.ViewSet):
#list_route()
#authentication_classes(SessionAuthentication)
#permission_classes(IsAuthenticated)
def connected(self, request):
return Response({"status": "ok"})
Even if the user is not authenticated (no session cookie), the action is performed.
As a work around, I've performed it like that :
class CommonView(viewsets.ViewSet):
#list_route()
def connected(self, request):
if request.user.is_authenticated():
return Response({"status": "ok"})
else:
return Response({"status": "ko", "message": "Unauthenticated"})
But I feel it could be cleaner, any idea ?
You can create a custom ListRouteIsAuthenticated permission class inheriting from BasePermission class which will deny any permission to unauthenticated users for any request in the list route.
For detail route requests, it will allow unrestricted access, regardless of if the request was authenticated or unauthenticated.
from rest_framework.permissions import BasePermission
class ListRouteIsAuthenticated(BasePermission):
"""
Custom Permission Class which authenticates a request for `list` route
"""
def has_permission(self, request, view):
if view.action == 'list':
return request.user and request.user.is_authenticated() # check user is authenticated for 'list' route requests
return True # no authentication check otherwise
Then in your viewset, you need to define this permission class.
class CommonView(viewsets.ViewSet):
permission_classes = [ListRouteIsAuthenticated]
...
According to the documentation, add an attribute:
class CommonView(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
I am trying to implement TokenAuthentication using the Rest Framework, but it seems that I can't add my own custom decorators to my ViewSets because they are evaluated BEFORE the authentication. Consider this:
from django.utils.decorators import method_decorator
from django.http.response import HttpResponseForbidden
def require_staff(View):
def staffOnly(function):
def wrap(request, *args, **kwargs):
if request.user.is_active and request.user.is_staff:
return function(request, *args, **kwargs)
else:
return HttpResponseForbidden()
return wrap
View.dispatch = method_decorator(staffOnly)(View.dispatch)
return View
When I try to implement this, it seems the decorator code fires first, so the authentication is never run.
#require_staff
class CustomerViewSet(ModelViewSet):
model = Customer
filter_class = CustomerFilter
filter_backends = (DjangoFilterBackend,)
Since request.user is never set, introducing the decorator breaks authentication.
I think the issue is that Authentication is occuring the rest_frameworks dispatch() function and it is not clear to me how I could add additional (say) custom security if authentication is done that late in the game.
Am I missing something here, or what is the proper way to implement this customization?
Someone suggested using Permissions for this instead. I assume they mean custom DRF Permissions, right?
Everything you need to know is DRF permissions is here: http://www.django-rest-framework.org/api-guide/permissions
DRF provides a built in permission that is similar to yours, called IsAdminUser
The IsAdminUser permission class will deny permission to any user,
unless user.is_staff is True in which case permission will be allowed.
This permission is suitable is you want your API to only be accessible
to a subset of trusted administrators.
To use this permission in a Class Based View:
class ExampleView(APIView):
permission_classes = (IsAdminUser,)
Now you have two options to do an extra check for user.is_active.
The first is override the IsAdminUser permission, like so:
from rest_framework import permissions
class IsActiveAndAdminUser(permissions.IsAdminUser):
"""Only allow a user who is Admin and Active to view this endpoint. """
def has_permission(self, request, view):
is_admin = super(IsAdminAndActiveUser, self).has_permission(request, view)
return request.user.is_active and is_admin
The second is to create an IsActiveUser permission, and chain them in your view.
IsActiveUser Permission:
from rest_framework import permissions
class IsActiveUser(permissions.BasePermission):
""" Only Active Users have permission """
def has_permission(self, request, view):
return request.user.is_active
Your new permission list in your class based view:
class ExampleView(APIView):
permission_classes = (IsActiveUser, IsAdminUser,)