Z Smith
Z Smith

Reputation: 252

Django User.set_password leads to invalid password

I'm trying to write a serializer (in Django REST Framework) to update a user's account details. Here is the update() method:

def update(self, instance, validated_data):
    ...
    if all([item in self.validated_data for item in ["password", "confirm_password", "old_password"]]):
        user = authenticate(username=self.context["request"].user.username, password=self.validated_data["old_password"])
        if user is not None:
            if self.validated_data["password"] == self.validated_data["confirm_password"]:
                validate_password(self.validated_data["password"])
                user.set_password(self.validated_data["password"])
                user.save()
            else:
                raise serializers.ValidationError({"confirm_password": "Passwords do not match"})
        else:
            raise serializers.ValidationError({"old_password": "Password incorrect"})

    self.validated_data.pop("password")

    return super(UserInfoSerializer, self).update(instance, validated_data)

When I perform a PATCH request to the view with "password", "confirm_password" and "old_password" as fields, it appears to have worked. Then when I try to log into the account again, it fails (using both old and new passwords). When I check the admin settings and view the user I am trying to edit, I get the following:

Invalid password format or unknown hashing algorithm.

Raw passwords are not stored, so there is no way to see this user's password, but you can change the password using this form.

I believe User.set_password() is supposed to handle hashing/etc. automatically, so why do I get this error?

Upvotes: 1

Views: 580

Answers (2)

Z Smith
Z Smith

Reputation: 252

In case anyone is interested in using my code, here is the final working code:

def update(self, instance, validated_data):
    ...
    if all([item in validated_data for item in ["password", "confirm_password", "old_password"]]):
        user = authenticate(username=instance.username, password=validated_data["old_password"])
        if user is not None and user == instance:
            if validated_data["password"] == validated_data["confirm_password"]:
                validate_password(validated_data["password"])
                instance.set_password(validated_data["password"])
                instance.save()  # change the password on the current instance object, otherwise changes will be overwritten
                login(self.context["request"], instance)  # without this line, the user is auto-logged out upon changing their password
            else:
                raise serializers.ValidationError({"confirm_password": "Passwords do not match"})
        else:
            raise serializers.ValidationError({"old_password": "Password incorrect"})

    if "password" in validated_data:
        validated_data.pop("password")

    return super(UserInfoSerializer, self).update(instance, validated_data)

Upvotes: 1

neverwalkaloner
neverwalkaloner

Reputation: 47354

You deleted password from self.validated_data but not from validated_data dict which passed to superclass's update method. Try this:

validated_data.pop("password") # remove self, just leave validated_data

return super(UserInfoSerializer, self).update(instance, validated_data)

Upvotes: 2

Related Questions