Reputation: 704
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.
Upvotes: 0
Views: 209
Reputation: 541
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
Upvotes: 3