New endpoint to update self account password

This commit is contained in:
Antoine Nguyen
2025-06-08 15:12:49 +02:00
parent 4cb15daf40
commit 8b1b59d8ba
6 changed files with 66 additions and 38 deletions
-33
View File
@@ -292,39 +292,6 @@ class AccountExistsSerializer(serializers.Serializer):
exists = serializers.BooleanField()
class AccountPasswordSerializer(serializers.ModelSerializer):
"""A serializer used to change a user password."""
new_password = serializers.CharField()
class Meta:
model = core_models.User
fields = (
"password",
"new_password",
)
def validate_password(self, value):
"""Check password."""
if not self.instance.check_password(value):
raise serializers.ValidationError("Password not correct")
return value
def validate_new_password(self, value):
"""Check new password."""
try:
password_validation.validate_password(value, self.instance)
except ValidationError as exc:
raise serializers.ValidationError(exc.messages[0]) from None
return value
def update(self, instance, validated_data):
"""Set new password."""
instance.set_password(validated_data["new_password"])
instance.save()
return instance
class WritableAccountSerializer(AccountSerializer):
"""Serializer to create account."""
+2 -1
View File
@@ -14,6 +14,7 @@ from rest_framework.response import Response
from modoboa.core import models as core_models
from modoboa.core import sms_backends
from modoboa.core.api.v1 import serializers as core_serializers
from modoboa.lib import renderers as lib_renderers
from modoboa.lib import viewsets as lib_viewsets
from modoboa.lib.throttle import GetThrottleViewsetMixin, PasswordResetRequestThrottle
@@ -112,7 +113,7 @@ class AccountViewSet(
action_dict = {
"list": serializers.AccountSerializer,
"retrieve": serializers.AccountSerializer,
"password": serializers.AccountPasswordSerializer,
"password": core_serializers.AccountPasswordSerializer,
"reset_password": serializers.ResetPasswordSerializer,
}
return action_dict.get(self.action, serializers.WritableAccountSerializer)
+10
View File
@@ -331,6 +331,16 @@ class AccountViewSetTestCase(ModoAPITestCase):
resp = self.client.patch(url, data, format="json")
self.assertEqual(resp.status_code, 200)
def test_user_updates_password(self):
account = core_models.User.objects.get(username="user@test.com")
self.authenticate_user(account)
response = self.client.post(
reverse("v2:account-set-me-password"),
{"password": "toto", "new_password": "Toto1234"},
format="json",
)
self.assertEqual(response.status_code, 200)
def test_rename(self):
account = core_models.User.objects.get(username="user@test.com")
url = reverse("v2:account-detail", args=[account.pk])
+37
View File
@@ -2,14 +2,51 @@
import logging
from django.contrib.auth import password_validation
from django.core.exceptions import ValidationError
from django.utils.html import escape
from django.utils.translation import gettext as _
from rest_framework import serializers
from modoboa.core import models
logger = logging.getLogger("modoboa.auth")
class AccountPasswordSerializer(serializers.ModelSerializer):
"""A serializer used to change a user password."""
new_password = serializers.CharField()
class Meta:
model = models.User
fields = (
"password",
"new_password",
)
def validate_password(self, value):
"""Check password."""
if not self.instance.check_password(value):
raise serializers.ValidationError("Password not correct")
return value
def validate_new_password(self, value):
"""Check new password."""
try:
password_validation.validate_password(value, self.instance)
except ValidationError as exc:
raise serializers.ValidationError(exc.messages[0]) from None
return value
def update(self, instance, validated_data):
"""Set new password."""
instance.set_password(validated_data["new_password"])
instance.save()
return instance
class CheckTFASetupSerializer(serializers.Serializer):
"""Serializer used to finalize 2FA setup."""
+14
View File
@@ -68,6 +68,20 @@ class AccountViewSet(core_v1_viewsets.AccountViewSet):
serializer.is_valid(raise_exception=True)
return response.Response()
@action(
methods=["post"],
detail=False,
url_path="me/password/change",
serializer_class=core_v1_serializers.AccountPasswordSerializer,
)
def set_me_password(self, request):
serializer = core_v1_serializers.AccountPasswordSerializer(
instance=request.user, data=request.data
)
serializer.is_valid(raise_exception=True)
serializer.save()
return response.Response()
@extend_schema(
description="Get current API token",
responses={200: serializers.UserAPITokenSerializer},
+3 -4
View File
@@ -160,10 +160,9 @@ class ProfileTestCase(LDAPTestCaseMixin, ModoAPITestCase):
username = "testuser@example.com"
self.authenticate(username, "test")
account = models.User.objects.get(username=username)
response = self.client.put(
reverse("v2:account-password", args=[account.id]),
{"password": "test", "newpassword": "Toto1234"},
response = self.client.post(
reverse("v2:account-set-me-password"),
{"password": "test", "new_password": "Toto1234"},
format="json",
)
self.assertEqual(response.status_code, 200)