harden OTP for real SMS and close the second audit round
Kavenegar is being switched on, so every OTP request now costs credit: - phone numbers are normalised to 09XXXXXXXXX everywhere (login, OTP, family invite, TEST_LOGIN_PHONES keys) and existing rows are migrated; non-Iranian and landline numbers are rejected before any SMS - cooldown and a daily per-phone cap are counted in the database, so the three gunicorn workers no longer triple them - a global daily OTP SMS cap (default 300) stops SMS pumping - per-IP rates widened for CGNAT / school Wi-Fi (env-overridable) - provider message id is logged; SMS text warns not to share the code Second audit round: an offer no longer shortens a longer paid plan, Zarinpal error bodies no longer 500, island XP reaches the parent report, a non-numeric questionId no longer 500s after the answer is generated. 596 tests green. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,8 @@ import re
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from .phone import normalize_phone
|
||||
|
||||
# لینک، آیدیِ شبکهی اجتماعی، و هر رشتهی ۵ رقمی یا بیشتر (شمارهی تلفن با
|
||||
# فاصله/خطتیره هم) — ارقامِ فارسی و عربی هم.
|
||||
_CONTACT = re.compile(
|
||||
@@ -15,6 +17,20 @@ def clean_display_name(value: str) -> str:
|
||||
return ' '.join(_CONTACT.sub(' ', value or '').split())[:30]
|
||||
|
||||
|
||||
class IranMobileField(serializers.CharField):
|
||||
"""CharField که خروجیاش همیشه 09XXXXXXXXX است."""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
kwargs.setdefault('max_length', 20)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def to_internal_value(self, data):
|
||||
phone = normalize_phone(super().to_internal_value(data))
|
||||
if phone is None:
|
||||
raise serializers.ValidationError('شمارهی موبایل معتبر نیست؛ مثلاً 09121234567.')
|
||||
return phone
|
||||
|
||||
|
||||
# ---- Request serializers ----
|
||||
|
||||
class GuestSessionRequestSerializer(serializers.Serializer):
|
||||
@@ -23,7 +39,7 @@ class GuestSessionRequestSerializer(serializers.Serializer):
|
||||
|
||||
|
||||
class LoginRequestSerializer(serializers.Serializer):
|
||||
phone = serializers.CharField(max_length=15)
|
||||
phone = IranMobileField()
|
||||
otp = serializers.CharField(max_length=6)
|
||||
|
||||
|
||||
@@ -91,7 +107,7 @@ class UpdateProfileRequestSerializer(serializers.Serializer):
|
||||
|
||||
class RequestOTPRequestSerializer(serializers.Serializer):
|
||||
"""اپ کمکی برای گرفتن OTP (در سند اصلی نبود، برای تستپذیری Swagger اضافه شد)."""
|
||||
phone = serializers.CharField(max_length=15)
|
||||
phone = IranMobileField()
|
||||
|
||||
|
||||
# ---- Response serializers ----
|
||||
|
||||
Reference in New Issue
Block a user