django rest framework: custom permission for GET request - django-rest-framework

i have this view handling only /GET/ request to display all authors only if the logged user is superuser
class AuthorView(generics.ListAPIView):
serializer_class = AuthorSerializer
queryset = Author.objects.all()
permission_class = (IsSuperUser,)
And the permission for superuser:
class IsSuperUser(permissions.BasePermission):
def has_permission(self, request, view):
user = request.user
return user.is_authenticated() and user.is_superuser
The permission does not seems to be working, placed the pdb in has_permission the control does not seems to coming there.
What am i missing ?

It should be:
permission_classes = (IsSuperUser,)
and not
permission_class = (IsSuperUser,)

Related

DRF how to call an api without the the queruset

I am trying to create a rest API in DRF without any DB. I want the user to hit the API with the post data. Once the system receives the data I will do another API call and show the response. My code is:
serializers.py
class getCPHSerializers(serializers.Serializer):
cph_id = serializers.CharField(max_length=128, write_only=True, required=True)
views.py
class GETCPHDetails(generics.ListAPIView):
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
def post(self, request, *args, **kwargs):
cphID = request.data.get('cphID',None)
errorList = []
if not cphID:
errorList.append({"message": "No CPH Found"})
if len(errorList) == 0:
param = {"searchQry": cphID}
apiResponse = requests.post("http://172.61.25.40:8000/api/newSearch", data=param )
return Response({"message":json.loads(apiResponse)})
I am getting an error
Expected a Response, HttpResponse or HttpStreamingResponse to be returned from the view, but received a <class 'NoneType'>
Any suggestion will be of great help. Thanks in advance.
You are seeing this error because you are not returning a response when
len(errorList) == 0 is False
Python will always return None if you don't return anything
from a method.

login by one user and accesstoken of another user

I have implemented simpleJWT for token based authentication. I created a simple hello world test API.
While testing, I am logging with /rest-auth/login/ and for generating use /api/token/ - both working fine.
Now for testing, I am logging in with say user XYZ (having access rights for helloworld api) and generating token using another user ABC (not having access rights for helloworld api).
So now user XYZ is authenticated (ok) but I am having token of user ABC (ok).
Now, when I call the API with token generated for use ABC, I am able to access the helloworld api even if user ABC has no rights for the API !! Because user XYZ who has rights already logged in.
Problem is this will always be case when multiple users will be using the site. How to resolve ? Few code snippets also presented below :
My settings.py snipped
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
}
code basically a decorator which authenticate for users is as below
def user_is_ADM(fun_c):
#wraps(fun_c)
def wrapped(request, *args, **kwargs):
# 1 = ADM
if(request.user and request.user.is_authenticated) : <--- here is the issue
user_data = UserProfile.objects.get(user_id = request.user.id)
# user profile as as a user type
u = user_data.user_type
if u == 1:
return fun_c(request, *args, **kwargs)
else:
raise PermissionDenied
return wrapped
what should be my strategy in this case ?
EDIT
Modified my decorator as follows and it is working. Someone please comment if I am doing something wrong
def user_is_ADM(fun_c):
#wraps(fun_c)
def wrapped(request, *args, **kwargs):
juser = get_user(request)
if juser.is_authenticated:
user_jwt = JWTAuthentication().authenticate(Request(request))
if user_jwt is not None:
if request.user == user_jwt[0]:
k = user_jwt[0].userprofile.get_user_type_display()
if k == 'ADM':
return fun_c(request,*args,**kwargs)
else:
raise PermissionDenied
else:
raise PermissionDenied
else:
raise PermissionDenied
else:
raise PermissionDenied
return wrapped
check out this documentation https://www.django-rest-framework.org/api-guide/permissions/ (Custom permissions)
when setting a general permission setting (IsAuthenticated).
It is really authenticating users but not verifying their permissions at any time
class IsAuthenticated(BasePermission):
"""
Allows access only to authenticated users.
"""
def has_permission(self, request, view):
return bool(request.user and request.user.is_authenticated)
if basic administrator and user authentication is not enough. you can implement a custom permission
from rest_framework import permissions
class CustomerAccessPermission(permissions.BasePermission):
"""
extracted from the documentation
"""
message = 'Adding customers not allowed.'
def has_permission(self, request, view):
"""
add authentication logic and return a boolean value
"""
# ...
# return bool()
in the views
from rest_framework.views import APIView
from modulename.permissions import CustomerAccessPermission
class ExampleView(APIView):
"""
...
"""
permission_classes = (CustomerAccessPermission,)
def get(self, request, format=None):
"""
...
"""
Here is an example with django authentication permissions
from typing import Tuple
from rest_framework.permissions import BasePermission
class CustomPermission(BasePermission):
"""
...
"""
list_permissions: Tuple[str] = (
'modelname.view_modelname',
'modelname.add_modelname',
'modelname.change_modelname',
'modelname.delete_modelname',
)
def has_permission(self, request, view) -> bool:
return bool(
request.user.has_perms(self.list_permissions)
or
request.user and request.user.is_staff
or
request.user and request.user.is_superuser
)
summary
With regard to the syntax in general, you will not see any changes, you must import your permission or decorator in the view, the difference lies in the runtime and the way django will evaluate the permissions
Remembering that a decorator is nothing more than a summary way of saying func(func()).
so you should always evaluate the view and call your method modified by the decorator
instead, permissions in this framework are always defined as a list of permission classes. Before executing the main body of the view, each permission in the list is verified. If any permission verification fails, exceptions will be generated. Permission denied or exceptions. An unauthenticated exception will be generated and the main body of the view will not be executed. (Consult the documentation for a complete explanation)
You should evaluate which method is most appropriate for your case (performance, syntax, etc.).
# you can set your permission in the general settings to avoid importing into each file
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'modulename.permissions.CustomPermission',
),
# ...
}

Django rest framework - exclude endpoint from authentication

I need to make view accessible without authentication, based on a variable passed in urls.py.
My idea is something like this:
urls.py
url(r'^oidc-api/', include('api.urls'), {'logged': True})
views.py
class ExampleViewSet(ModelViewSet):
if logged: # How can I get this variable, passed in urls.py?
permission_classes = () # This will exclude current view from authentication
queryset = Widget.objects.filter(visible=True)
serializer_class = ExampleSerializer
filter_backends = (DjangoFilterBackend,)
filter_fields = ('example_id',)
However, I can not access logged variable that is passed from urls.py.
John
You can't do that.
What you could do is, remove those permission classes from the views manually or set those to an empty set as,
class ExampleViewSet(ModelViewSet):
permission_classes = ()

Post from Angular 5 front-end to Django Rest Framework back-end

I am trying to create a post request from Angular 5 to DRF. The field is a form that a user submits. I created the serializer and i can post something from DRF interface
models.py
class UserForm(models.Model):
id_user_form = models.AutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, db_column='idUser', unique=False)
name = models.CharField(max_length=50)
type = models.CharField(max_length=25)
location = models.CharField(max_length=200)
serilizers.py
`class UserFormSerializer(serializers.ModelSerializer):
class Meta:
model = UserForm
fields = ('user', 'name', 'type', 'location')
def create(self, validated_data):
user_data = validated_data.pop('user')
user_form = UserForm.objects.create(user_id=user_data, **validated_data)
return user_form
views.py
class FormSubmit(generics.ListCreateAPIView):
queryset = UserForm.objects.all()
serializer_class = UserFormSerializer
When i try to post it via Angular I get this error:
Forbidden (CSRF token missing or incorrect.): /api/form/
Am I doing something wrong?
Had the exact same problem when I wanted to upload a profile picture to my Django REST backend.
You have 2 options, basically.
The first one is disabling the CSRF checks, which are by default enforced by DRF. Have a read here. You may do this for testing purposes.
The second option would be to pass the CSRF Token inside your request header. To do that, have a look at the $cookies API.
With that, you can get the token out of the cookie and paste it into your header like so: 'X-CSRFToken': your-csrf-token-here.
You can verify this by opening your dev tools inside the browser and navigating to your site's cookies. You should see one called csrftoken there.
I hope this sent you on the right track.

DjangoRestFramework - Where do I raise a 404 error when `get_queryset()` cannot get an object because it does not exist?

This is my view:
class PostListByUsername(generics.ListAPIView):
serializer_class = PostSerializer
permission_classes = (IsAuthenticated, IsLikeOrOwnerDeleteOrReadOnly, IsFromSameLocation,)
def get_queryset(self):
username = self.kwargs['username']
user = User.objects.get(username=username)
return Post.objects.filter(owner__username=username).order_by('-createdAt')
This is my IsFromSameLocation permission:
class IsFromSameLocation(permissions.BasePermission):
"""
Permission.
Allow permissions to authenticated users from the same
location as the user.
"""
message = 'Not from the same location.'
def has_permission(self, request, view):
username = view.kwargs.get('username', None)
try:
userProfile = User.objects.get(username=username)
except:
return False
return request.user.userextended.location == userProfile.userextended.location
With that said, in my get_queryset() method, I do user = User.objects.get(username=username) but if the user does not exist, I want to raise a 404 error. I know that get_queryset() is supposed to return a queryset, so I'm guessing I shouldn't raise a 404 in that method. So where exactly should I do the check to see if the user exists or not? Note that in my permission, I do do a try and except to see if the user exists, but permissions should only return True or False from my understanding (not raise 404 errors).
You actually can raise an exception from a permission and it will be correctly handled by Django. For example you can do it with get_object_or_404() shortcut function:
def has_permission(self, request, view):
username = view.kwargs.get('username', None)
userProfile = get_object_or_404(User, username=username)
return request.user.userextended.location == userProfile.userextended.location
In fact, while the code that throws an exception is executed in a view, it will be handled by Django, so it should not matter where you are raising it from -- from view, serializer, permission, model etc. methods.
The easiest way is to raise a Http404 exception:
In your try/except block, raise the 404 in the exception, but its probably worth limiting the exception to just DoesNotExist errors:
try:
userProfile = User.objects.get(username=username)
except User.DoesNotExist:
from django.http import Http404
raise Http404("User does not exist")

Resources