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'
Related
I was using Django user model using the ModelViewSet. When I am making a request to update the password for the current user that is logged in. Although I get a 200 OK response but my password never changes to the new one that I changed.
I also tried making the request from my admin user and when I made the PUT request with the password, it got changed to something else and I was logged out from the django admin panel.
Here is my
views.py
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAuthenticated, IsOwnerOfObject]
authentication_classes = (TokenAuthentication,)
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'password']
extra_kwargs = {
'password' : {
'write_only':True,
'required': True
}
}
def create(self, validated_data):
user = User.objects.create_user(**validated_data)
Token.objects.create(user=user) # create token for the user
return user
urls.py
router = DefaultRouter()
router.register('articles', ArticleViewSet, basename='articles')
router.register('users', UserViewSet, basename = 'users')
urlpatterns = [
path('api/', include(router.urls)),
]
permissions.py
class IsOwnerOfObject(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return obj == request.user
Here is how I am making the request, with Authorisation token in the Headers field
Response :
As #BrianDestura says, you need call set_password to correctly update it.
class UserSerializer(serializers.ModelSerializer):
# --> Add this method
def update(self, instance, validated_data):
# We Can change the username?
instance.username = validated_data['username']
instance.set_password(validated_data['password'])
instance.save()
return instance
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]
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'
]
}
I tried to change password without the traditional way. Give old password and new password, update the old password. I used UpdateApiView.But i get the following error.
Django doesn't provide a DB representation for AnonymousUser
I tried passigng Authorization token in header using POST MAN. But Same error.
view.py
class ChangePassword(generics.UpdateAPIView):
serializer_class = serializers.ChangePasswordSerializer
model = models.Customer
def get_object(self, queryset=None):
obj = self.request.user
return obj
def update(self, request, *args, **kwargs):
self.object = self.get_object()
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
if not self.object.check_password(serializer.data.get("old_password")):
return Response({"old_password": ["Wrong password."]}, status=HTTP_400_BAD_REQUEST)
self.object.set_password(serializer.data.get("new_password"))
self.object.save()
return Response("Success.", status=HTTP_200_OK)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
serializers.py
class ChangePasswordSerializer(serializers.Serializer):
old_password = serializers.CharField(required=True)
new_password = serializers.CharField(required=True)
urls.py
path('update_password', views.ChangePassword.as_view()),
EDIT:
I added TokenAuthentication in settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
In views.py, i added
authentication_classes = (TokenAuthentication, )
Now i got name 'TokenAuthentication' is not defined
I imported TokenAuthentication in views.py
from rest_framework.authentication import SessionAuthentication, TokenAuthentication
But Django doesn't provide a DB representation for AnonymousUser.
Add the attribute authentication_classes = (TokenAuthentication, ) to ChangePassword view.
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