Custom permission not passing to generic views django rest - python

I am writing custom permission and inheriting in the generic views but it is not working it returns
TypeError: Cannot cast AnonymousUser to int. Are you trying to use it in place of User?
instead of User required to perform this action
my custom permission as follow
class IsOwnedByUser(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
message = {'detail': 'User required to perform this action'}
return obj.user == request.user
views.py
class OrderAddressView(generics.ListCreateAPIView):
queryset = OrderAddress.objects.all()
serializer_class = OrderAddressSerializer
permission_classes = (IsOwnedByUser,)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
def get_queryset(self):
return OrderAddress.objects.filter(user=self.request.user)
BTW, it works fine with default rest framework permission class when I use like this
permission_class = (permissions.IsAuthenticated,)
but with my custom permission it is not working( Can anyone tell why it is that? Thanks in advance!

Actually, as Pavan Kumar told you, you should use both (permissions.IsAuthenticated, IsOwnedByUser)
But if you want to use only one permission, you can create your own:
class IsAuthenticatedAndOwner(permissions.BasePermission):
message = 'User required to perform this action'
def has_permission(self, request, view):
return request.user and request.user.is_authenticated
def has_object_permission(self, request, view, obj):
return obj.user == request.user

You can also add a default in your settings.py file. Try with the following (adjust the permissions as you want though as in docs):
REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated'
]
}

Related

Django : class based view with multiple permissions

I am trying to create a class based view with different permissions per function. I have already created my permissions in a file that I import :
utils.py
from rest_framework.permissions import BasePermission
from rest_framework.authentication import TokenAuthentication
from rest_framework.views import APIView
class IsOwner(BasePermission):
"""
Check if the user who made the request is owner.
Use like that : permission_classes = [IsOwner]
"""
def has_object_permission(self, request, view, obj):
# if request.method in permissions.SAFE_METHODS:
# return True
return obj.user == request.user
class IsAdmin(BasePermission):
"""
Check if the user who made the request is admin.
Use like that : permission_classes = [IsAdmin]
"""
def has_permission(self, request, view):
return request.user.is_admin
class BaseView(APIView):
"""
Check if a user is authenticated
"""
authentication_classes = [
TokenAuthentication,
]
class AdminOrOwnerView(APIView):
"""
Check if a user is admin or owner
"""
authentication_classes = ( IsOwner | IsAdmin,)
I want to create a class with different method. The GET method would allow admins to have a view of all users. The POST method would allow any user to connect.
I have already created the serializer for each of these methods but I cannot assign different permissions per method.
This is my class :
class Users(APIView):
def get(self, request):
"""Only for admin"""
try:
user = Users.objects.all()
except User.DoesNotExist():
return HttpResponse(status=404)
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
def post(self, request):
"""For everyone"""
serializer = RegistrationSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.error)
How I can apply my permissions on each of the methods separately ?
Thank you in advance for your help
You just need to create a permission class like this:
class CustomPermissionClass(BasePermission):
def has_permission(self, request, view):
if request.method == 'GET':
# logic for GET method
elif request.method == 'POST'
# logic for POST metod
# default logic
And add it to your view:
class Users(APIView):
permission_classes = [CustomPermissionClass]

How to logout in rest_framework with JWT authentication?

How to log out in rest_framework?
This is my user serializer. I'm using rest and very very newbie. Sign up and Login working, but have no clue how to impelement logout.
class UserSerializer(serializers.ModelSerializer):
email = serializers.EmailField(write_only=True)
class Meta:
model = User
fields = ('id', 'username', 'password', 'email')
write_only_fields = ('username', 'email', 'password',)
read_only_fields = ('id', )
Sign up part
class UserCreateAPIView(generics.CreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [permissions.AllowAny]
Loggin code; i am using JWT authentication.
path(r'login/', obtain_jwt_token, name='ObtainJWTToken'),
When i use this code:
#api_view(['POST'])
def logout(request):
request.auth.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
i get error: 'bytes' object has no attribute 'delete'
When i use Djoser code:
permission_classes = settings.PERMISSIONS.token_destroy
def post(self, request):
utils.logout_user(request)
return Response(status=status.HTTP_204_NO_CONTENT)
getting this error: type object 'Token' has no attribute 'objects'
I assume you use TokenAuthentication.
In this case, give this method a shot:
class LogoutAPIView(APIView):
def get(self, request):
request.user.auth_token.delete()
return Response(status=status.HTTP_200_OK)
However, please note that this deletes the token and consequently forces a logout.
In case you use SessionAuthentication, the body of your get() method inside the LogoutAPIView class could be just logout(request).
eg.
def get(self, request):
logout(request)
return Response(status=status.HTTP_200_OK)
In the case of JWT authentication, as it is stateless and each JWT token is valid for some time, that means even if you remove the token from the DB, the user will still be able to use your endpoints for a short amount of time, until it's expired.
Therefore, depending on how deep you want to go, you can eg. implement a "tokens_unable_to_login" cache key and check if the requests include a JWT token that should not log in. Or you let it expire - you can toy around with the expiration time of the token and see what's ideal for your use-case. That's up to your business requirements.
Let me know how did this go for you.

Rest Framework serializers, forbid users to change others password

I'm creating a simple web app and I cannot find any way to forbid other users from changing your password. This is minimal code:
# serializers.py
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
def create(self, validated_data):
# have to use custom create method because default method calls `User.objects.create()` which doesn't take care of password hashing and other important stuff
return User.objects.create_user(**validated_data)
class Meta:
model = User
fields = ('id', 'username', 'email', 'password')
read_only_fields = ('id', 'username')
# password is set to write_only because I don't want to send it to anybode (even though it's just a hash)
extra_kwargs = {'password': {'write_only': True}}
# views.py
from .serializers import UserSerializer
from rest_framework import generics
from django.contrib.auth.models import User
class UserDetails(generics.RetrieveUpdateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
I could take care of this myself by using APIView.
# views.py
from .serializers import UserSerializer
from rest_framework.views import APIView
from django.contrib.auth.models import User
class UserDetails(APIView):
def put(self, request, format=None):
serialized = UserSerializer(data=request.DATA)
if not serialized.is_valid():
return # probably HTTP 400 Error code
if request.user.id != serialized.data['id']:
# this if is what I'm trying to achieve
return # probably HTTP 403 Error code
user = User.objects.update(
id=serialized.data['id'],
email=serialized.data['email']
)
if 'password' in request.DATA:
user.set_password(request.DATA['password'])
return # probably HTTP 200 Error code
Unfortunately, that would have caused that scheme generated by rest_framework.schemas.get_schema_view would be incomplete. And I use for communication CoreAPI (which cannot communicate with something that is not described in the scheme) so I cannot do that. I haven't found anything in the official documentation.
This seems to be a too basic problem that has a super easy solution that I missed. Thanks for any ideas or places where to look.
PS: I'm using django2.1 with python3.6
Edit: osobacho's solutions is clean and works like charm. Anyway I also need to allow modifications (let's say of TODO-list) only to creator of that todo list. Thought that solution for password problem would be applicable but it's not.
You can get the user in the request. That way each user will only be able to change their own password.
class UserDetails(generics.RetrieveUpdateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
def get_object(self):
return self.request.user
For your second question take a look at django_restframework http://www.django-rest-framework.org/api-guide/permissions/ permissions here is an example:
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Object-level permission to only allow owners of an object to edit it.
Assumes the model instance has an `owner` attribute.
"""
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:
return True
# Instance must have an attribute named `owner`.
return obj.owner == request.user
then you need to add to your view:
permission_classes = (IsOwnerOrReadOnly,)
hope it helps

Limit user to access a APIView

I want to get the user's detail information:
class UserDetailAPIView(RetrieveAPIView):
"""
User detail information
"""
queryset = User.objects.filter(is_valid=True).exclude(status=4)
serializer_class = UserDetailSerializer
lookup_field = "username"
I want to limit other users to access this APIView, I want only admin user and the user it self to access that.
How to limit this?
you should define your own permission class.something like this:
from rest_framework import permissions
class OwnerProfilePermission(permissions.BasePermission):
"""object lvl permissions for owner """
def has_object_permission(self, request, view, obj):
return obj.user == request.user
and in your views include permission_classes .see DRF documention.
http://www.tomchristie.com/rest-framework-2-docs/api-guide/permissions
and the class base views you choose is important.
http://www.tomchristie.com/rest-framework-2-docs/api-guide/generic-views

Django Rest Framework - authentication_classes by method in viewsets

I want to disable authentication on creation in my UserViewSet, so that even non authenticated user can create an account.
I'm using django-oauth-toolkit to authenticate in my application, and I use their authentication class as default in my settings (which is great)
I have tried to use the #authentication_class decorator (https://stackoverflow.com/a/39717881/5438372), but it doesn't seem to work with ModelViewSet
And I also tried to override the get_authenticator method, in the same spirit as this : Django rest framework permission_classes of ViewSet method, but ViewSet.action doesn't seem to be available at authentication.
How can I do this ? I there something wrong with my approach ?
Here is my code :
<models.py:>
class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
permission_classes = (IsSelfOrStaffPermission, TokenHasReadWriteScope,)
lookup_field = 'username'
def get_queryset(self):
current_user = self.request.user
if current_user.is_staff:
user_set = User.objects.all()
else:
user_set = User.objects.filter(username=current_user.username)
query = self.request.query_params.get('q', None)
if not query:
return user_set
return user_set.filter(
Q(username__icontains=query) |
Q(first_name__icontains=query) |
Q(last_name__icontains=query)
)
<settings.py:>
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)
}
<permission.py:>
class IsSelfOrStaffPermission(permissions.BasePermission):
"""
Permission to allow user actions on his own profile
"""
def has_object_permission(self, request, view, obj):
return obj == request.user or request.user.is_staff
You can check if view.action is 'create' or not inside has_permission method:
class IsSelfOrStaffPermission(permissions.BasePermission):
"""
Permission to allow user actions on his own profile
"""
def has_permission(self, request, view):
return view.action == 'create'

Categories

Resources