fix(users): validate password reset mobile input

This commit is contained in:
2026-05-03 17:10:00 +03:30
parent 0823267544
commit 8ff1e4fa61
2 changed files with 48 additions and 8 deletions

View File

@@ -6,6 +6,11 @@ from core.serializers.base import BaseModelSerializer
User = get_user_model()
INVALID_MOBILE_FORMAT_MESSAGE = "فرمت شماره موبایل نادرست است."
INVALID_MOBILE_NUMBER_MESSAGE = "شماره موبایل معتبر نیست."
PASSWORD_MISMATCH_MESSAGE = "رمز عبور مطابقت ندارد."
NEW_PASSWORD_MISMATCH_MESSAGE = "رمز عبور جدید و تکرار آن مطابقت ندارند."
class UserProfilePictureSerializer(BaseModelSerializer):
class Meta:
@@ -39,10 +44,10 @@ class RegisterSerializer(serializers.Serializer):
re_password = data.get("re_password", "")
if not (mobile.isdigit() and len(mobile) == 11):
raise serializers.ValidationError({"mobile": "فرمت شماره موبایل نادرست است."})
raise serializers.ValidationError({"mobile": INVALID_MOBILE_FORMAT_MESSAGE})
if password != re_password:
raise serializers.ValidationError({"password": "رمز عبور مطابقت ندارد."})
raise serializers.ValidationError({"password": PASSWORD_MISMATCH_MESSAGE})
return data
@@ -54,7 +59,7 @@ class SendOTPSerializer(serializers.Serializer):
def validate_mobile(self, value):
if not value.isdigit() or len(value) != 11 or not value.startswith("09"):
raise serializers.ValidationError("شماره موبایل معتبر نیست.")
raise serializers.ValidationError(INVALID_MOBILE_NUMBER_MESSAGE)
return value
@@ -65,7 +70,7 @@ class LoginOtpSerializer(serializers.Serializer):
def validate_mobile(self, value):
if not (value.isdigit() and len(value) == 11):
raise serializers.ValidationError("فرمت شماره موبایل نادرست است.")
raise serializers.ValidationError(INVALID_MOBILE_FORMAT_MESSAGE)
return value
@@ -75,7 +80,7 @@ class LoginSerializer(serializers.Serializer):
def validate_mobile(self, value):
if not (value.isdigit() and len(value) == 11):
raise serializers.ValidationError("فرمت شماره موبایل نادرست است.")
raise serializers.ValidationError(INVALID_MOBILE_FORMAT_MESSAGE)
return value
@@ -90,7 +95,7 @@ class GoogleOAuthCompleteSerializer(serializers.Serializer):
def validate_mobile(self, value):
normalized = "".join(ch for ch in value if ch.isdigit())
if len(normalized) != 11 or not normalized.startswith("09"):
raise serializers.ValidationError("فرمت شماره موبایل نادرست است.")
raise serializers.ValidationError(INVALID_MOBILE_FORMAT_MESSAGE)
return normalized
@@ -105,9 +110,14 @@ class ResetPasswordSerializer(serializers.Serializer):
password = serializers.CharField(write_only=True)
re_password = serializers.CharField(write_only=True)
def validate_mobile(self, value):
if not value.isdigit() or len(value) != 11 or not value.startswith("09"):
raise serializers.ValidationError(INVALID_MOBILE_NUMBER_MESSAGE)
return value
def validate(self, data):
if data.get("password") != data.get("re_password"):
raise serializers.ValidationError({"password": "رمز عبور مطابقت ندارد."})
raise serializers.ValidationError({"password": PASSWORD_MISMATCH_MESSAGE})
return data
@@ -118,7 +128,8 @@ class ChangePasswordSerializer(serializers.Serializer):
def validate(self, data):
if data.get("new_password") != data.get("re_password"):
raise serializers.ValidationError({"new_password": "رمز عبور جدید و تکرار آن مطابقت ندارند."})
raise serializers.ValidationError({"new_password": NEW_PASSWORD_MISMATCH_MESSAGE})
return data

View File

@@ -65,6 +65,20 @@ class UserApiViewTests(APITestCase):
mode="login",
)
@patch("apps.users.api.views.generate_and_send_otp")
def test_send_otp_view_supports_forget_password_mode(self, generate_and_send_otp):
response = self.client.post(
"/api/users/otp/send/",
{"mobile": "09123330001", "mode": "forget_password"},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
generate_and_send_otp.assert_called_once_with(
mobile="09123330001",
mode="forget_password",
)
@patch("apps.users.api.views.login_with_password")
def test_login_view_returns_tokens(self, login_with_password):
login_with_password.return_value = {"access": "a", "refresh": "r"}
@@ -112,6 +126,21 @@ class UserApiViewTests(APITestCase):
password="new-secret123",
)
def test_reset_password_view_rejects_invalid_mobile_format(self):
response = self.client.post(
"/api/users/password/reset/",
{
"mobile": "9123330001",
"code": "123456",
"password": "new-secret123",
"re_password": "new-secret123",
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn("error", response.data)
@patch("apps.users.api.views.change_password")
def test_change_password_view_requires_auth_and_calls_service(self, change_password):
self.client.force_authenticate(user=self.user)