fix(users): validate password reset mobile input
This commit is contained in:
@@ -6,6 +6,11 @@ from core.serializers.base import BaseModelSerializer
|
|||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
INVALID_MOBILE_FORMAT_MESSAGE = "فرمت شماره موبایل نادرست است."
|
||||||
|
INVALID_MOBILE_NUMBER_MESSAGE = "شماره موبایل معتبر نیست."
|
||||||
|
PASSWORD_MISMATCH_MESSAGE = "رمز عبور مطابقت ندارد."
|
||||||
|
NEW_PASSWORD_MISMATCH_MESSAGE = "رمز عبور جدید و تکرار آن مطابقت ندارند."
|
||||||
|
|
||||||
|
|
||||||
class UserProfilePictureSerializer(BaseModelSerializer):
|
class UserProfilePictureSerializer(BaseModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -39,10 +44,10 @@ class RegisterSerializer(serializers.Serializer):
|
|||||||
re_password = data.get("re_password", "")
|
re_password = data.get("re_password", "")
|
||||||
|
|
||||||
if not (mobile.isdigit() and len(mobile) == 11):
|
if not (mobile.isdigit() and len(mobile) == 11):
|
||||||
raise serializers.ValidationError({"mobile": "ÙØ±Ù…ت شماره موبایل نادرست است."})
|
raise serializers.ValidationError({"mobile": INVALID_MOBILE_FORMAT_MESSAGE})
|
||||||
|
|
||||||
if password != re_password:
|
if password != re_password:
|
||||||
raise serializers.ValidationError({"password": "رمز عبور مطابقت ندارد."})
|
raise serializers.ValidationError({"password": PASSWORD_MISMATCH_MESSAGE})
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@@ -54,7 +59,7 @@ class SendOTPSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
def validate_mobile(self, value):
|
def validate_mobile(self, value):
|
||||||
if not value.isdigit() or len(value) != 11 or not value.startswith("09"):
|
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
|
return value
|
||||||
|
|
||||||
|
|
||||||
@@ -65,7 +70,7 @@ class LoginOtpSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
def validate_mobile(self, value):
|
def validate_mobile(self, value):
|
||||||
if not (value.isdigit() and len(value) == 11):
|
if not (value.isdigit() and len(value) == 11):
|
||||||
raise serializers.ValidationError("ÙØ±Ù…ت شماره موبایل نادرست است.")
|
raise serializers.ValidationError(INVALID_MOBILE_FORMAT_MESSAGE)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
@@ -75,7 +80,7 @@ class LoginSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
def validate_mobile(self, value):
|
def validate_mobile(self, value):
|
||||||
if not (value.isdigit() and len(value) == 11):
|
if not (value.isdigit() and len(value) == 11):
|
||||||
raise serializers.ValidationError("ÙØ±Ù…ت شماره موبایل نادرست است.")
|
raise serializers.ValidationError(INVALID_MOBILE_FORMAT_MESSAGE)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
@@ -90,7 +95,7 @@ class GoogleOAuthCompleteSerializer(serializers.Serializer):
|
|||||||
def validate_mobile(self, value):
|
def validate_mobile(self, value):
|
||||||
normalized = "".join(ch for ch in value if ch.isdigit())
|
normalized = "".join(ch for ch in value if ch.isdigit())
|
||||||
if len(normalized) != 11 or not normalized.startswith("09"):
|
if len(normalized) != 11 or not normalized.startswith("09"):
|
||||||
raise serializers.ValidationError("ÙØ±Ù…ت شماره موبایل نادرست است.")
|
raise serializers.ValidationError(INVALID_MOBILE_FORMAT_MESSAGE)
|
||||||
return normalized
|
return normalized
|
||||||
|
|
||||||
|
|
||||||
@@ -105,9 +110,14 @@ class ResetPasswordSerializer(serializers.Serializer):
|
|||||||
password = serializers.CharField(write_only=True)
|
password = serializers.CharField(write_only=True)
|
||||||
re_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):
|
def validate(self, data):
|
||||||
if data.get("password") != data.get("re_password"):
|
if data.get("password") != data.get("re_password"):
|
||||||
raise serializers.ValidationError({"password": "رمز عبور مطابقت ندارد."})
|
raise serializers.ValidationError({"password": PASSWORD_MISMATCH_MESSAGE})
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
@@ -118,7 +128,8 @@ class ChangePasswordSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
if data.get("new_password") != data.get("re_password"):
|
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
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,20 @@ class UserApiViewTests(APITestCase):
|
|||||||
mode="login",
|
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")
|
@patch("apps.users.api.views.login_with_password")
|
||||||
def test_login_view_returns_tokens(self, login_with_password):
|
def test_login_view_returns_tokens(self, login_with_password):
|
||||||
login_with_password.return_value = {"access": "a", "refresh": "r"}
|
login_with_password.return_value = {"access": "a", "refresh": "r"}
|
||||||
@@ -112,6 +126,21 @@ class UserApiViewTests(APITestCase):
|
|||||||
password="new-secret123",
|
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")
|
@patch("apps.users.api.views.change_password")
|
||||||
def test_change_password_view_requires_auth_and_calls_service(self, change_password):
|
def test_change_password_view_requires_auth_and_calls_service(self, change_password):
|
||||||
self.client.force_authenticate(user=self.user)
|
self.client.force_authenticate(user=self.user)
|
||||||
|
|||||||
Reference in New Issue
Block a user