Remove the guest system entirely: no is_guest/device_id, no guest throttles, phone login is the only way in
accounts.0008 drops the columns and refuses to run while guest rows exist. Docs describe the current state; tests that only proved guest behaviour are gone. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -152,7 +152,7 @@ class BazaarVerifyPurchaseThrottleTests(TestCase):
|
||||
(throttle_scope='bazaar_verify'): هر درخواستِ پذیرفتهشده یک تماسِ
|
||||
سرور-به-سرورِ واقعی با API بازار میزنه؛ بدونِ throttle مخصوص، یک
|
||||
کاربرِ احرازهویتشده میتونست با ارسالِ مکررِ purchaseToken بارِ زیادی
|
||||
روی سرویسِ بازار/بکاند بندازه. GuestAwareBazaarThrottle با pk کاربرِ
|
||||
روی سرویسِ بازار/بکاند بندازه. throttle با pk کاربرِ
|
||||
احرازهویتشده کلید میخوره (نه IP)، پس تغییرِ IP این محدودیت رو دور
|
||||
نمیزنه.
|
||||
"""
|
||||
@@ -160,7 +160,7 @@ class BazaarVerifyPurchaseThrottleTests(TestCase):
|
||||
def setUp(self):
|
||||
cache.clear()
|
||||
self.client = APIClient()
|
||||
self.user = User.objects.create(device_id='device-bazaar-throttle-1', is_guest=True, name='')
|
||||
self.user = User.objects.create(phone='09000000007', name='')
|
||||
self.token = issue_access_token(self.user.id)
|
||||
|
||||
def _make_order(self, key):
|
||||
@@ -211,7 +211,7 @@ class BazaarVerifyPurchaseThrottleTests(TestCase):
|
||||
def test_changing_client_ip_does_not_bypass_the_user_limit(self):
|
||||
# کاربرِ غیرمهمان (نه self.user که مهمانه)، چون کلیدِ throttleِ
|
||||
# مهمانها عمداً بر اساسِ IP است؛ این تست فقط رفتارِ per-user رو میسنجه.
|
||||
registered_user = User.objects.create(is_guest=False, name='', phone='+989120000751')
|
||||
registered_user = User.objects.create(name='', phone='+989120000751')
|
||||
registered_token = issue_access_token(registered_user.id)
|
||||
|
||||
with patch.dict(ScopedRateThrottle.THROTTLE_RATES, {'bazaar_verify': '3/hour'}):
|
||||
@@ -256,29 +256,13 @@ class BazaarVerifyPurchaseThrottleTests(TestCase):
|
||||
self.assertFalse(Transaction.objects.filter(order=second_order).exists())
|
||||
|
||||
|
||||
class BazaarVerifyPurchaseGuestThrottleBypassTests(TestCase):
|
||||
"""
|
||||
🔒 اثبات فیکسِ امنیتی: قبلاً کلیدِ throttle_scope='bazaar_verify' برای
|
||||
هر کاربرِ احرازهویتشده (مهمان یا نه) user.pk بود. چون GuestSessionView
|
||||
با هر deviceId جدید یک کاربرِ مهمانِ تازه (pk جدید) میسازه، یک مهاجم
|
||||
میتونست با ساختنِ پیدرپیِ حسابهای مهمان، هر بار یک bucket throttle
|
||||
تازه برای تریگر کردنِ تماسِ سرور-به-سرورِ verify_purchase بگیره. حالا
|
||||
GuestAwareBazaarThrottle برای کاربرهای مهمان کلید رو بر اساسِ IP
|
||||
میسازه، پس چرخشِ حساب مهمان دیگه bucket تازه نمیده. رفتارِ throttle
|
||||
برای کاربرِ غیرمهمان (per-user، مثلِ قبل) دستنخورده مونده.
|
||||
"""
|
||||
class BazaarVerifyPurchasePerUserThrottleTests(TestCase):
|
||||
"""هر کاربرِ ثبتنامشده سطلِ throttleِ جدای خودش را دارد؛ مصرفِ حسابِ دیگر به او نمیرسد."""
|
||||
|
||||
def setUp(self):
|
||||
cache.clear()
|
||||
self.client = APIClient()
|
||||
|
||||
def _make_guest_with_order(self, device_id, key):
|
||||
guest = User.objects.create(device_id=device_id, is_guest=True, name='')
|
||||
order = Order.objects.create(
|
||||
user_id=guest.id, amount=10000, provider='bazaar',
|
||||
bazaar_product_sku='sku_one_time', idempotency_key=key,
|
||||
)
|
||||
return guest, order
|
||||
|
||||
def _verify(self, order_id, purchase_token, token):
|
||||
return self.client.post(
|
||||
@@ -288,32 +272,13 @@ class BazaarVerifyPurchaseGuestThrottleBypassTests(TestCase):
|
||||
HTTP_AUTHORIZATION=f'Bearer {token}',
|
||||
)
|
||||
|
||||
def test_rotating_guest_accounts_share_the_same_throttle_bucket(self):
|
||||
with patch.dict(ScopedRateThrottle.THROTTLE_RATES, {'bazaar_verify': '2/hour'}):
|
||||
with patch('payment.views.bazaar.verify_purchase') as mock_verify:
|
||||
mock_verify.return_value = BazaarPurchaseResult(is_valid=True, purchase_state=0)
|
||||
|
||||
guest1, order1 = self._make_guest_with_order('device-bazaar-guest-rot-1', 'order-bazaar-rot-1')
|
||||
response1 = self._verify(order1.id, 'token-rot-1', issue_access_token(guest1.id))
|
||||
self.assertEqual(response1.status_code, 200)
|
||||
|
||||
guest2, order2 = self._make_guest_with_order('device-bazaar-guest-rot-2', 'order-bazaar-rot-2')
|
||||
response2 = self._verify(order2.id, 'token-rot-2', issue_access_token(guest2.id))
|
||||
self.assertEqual(response2.status_code, 200)
|
||||
|
||||
# سومین حسابِ مهمانِ تازه (pk جدید) باید throttle بشه، چون
|
||||
# bucket بر اساسِ IP بهاشتراک گذاشته شده، نه user.pk.
|
||||
guest3, order3 = self._make_guest_with_order('device-bazaar-guest-rot-3', 'order-bazaar-rot-3')
|
||||
response3 = self._verify(order3.id, 'token-rot-3', issue_access_token(guest3.id))
|
||||
|
||||
self.assertEqual(response3.status_code, 429)
|
||||
|
||||
def test_registered_users_keep_independent_per_user_buckets(self):
|
||||
with patch.dict(ScopedRateThrottle.THROTTLE_RATES, {'bazaar_verify': '1/hour'}):
|
||||
with patch('payment.views.bazaar.verify_purchase') as mock_verify:
|
||||
mock_verify.return_value = BazaarPurchaseResult(is_valid=True, purchase_state=0)
|
||||
|
||||
user_a = User.objects.create(is_guest=False, name='', phone='+989120000761')
|
||||
user_a = User.objects.create(name='', phone='+989120000761')
|
||||
order_a = Order.objects.create(
|
||||
user_id=user_a.id, amount=10000, provider='bazaar',
|
||||
bazaar_product_sku='sku_one_time', idempotency_key='order-bazaar-reg-a',
|
||||
@@ -321,7 +286,7 @@ class BazaarVerifyPurchaseGuestThrottleBypassTests(TestCase):
|
||||
response_a = self._verify(order_a.id, 'token-reg-a', issue_access_token(user_a.id))
|
||||
self.assertEqual(response_a.status_code, 200)
|
||||
|
||||
user_b = User.objects.create(is_guest=False, name='', phone='+989120000762')
|
||||
user_b = User.objects.create(name='', phone='+989120000762')
|
||||
order_b = Order.objects.create(
|
||||
user_id=user_b.id, amount=10000, provider='bazaar',
|
||||
bazaar_product_sku='sku_one_time', idempotency_key='order-bazaar-reg-b',
|
||||
@@ -330,26 +295,6 @@ class BazaarVerifyPurchaseGuestThrottleBypassTests(TestCase):
|
||||
|
||||
self.assertEqual(response_b.status_code, 200)
|
||||
|
||||
def test_blocked_guest_request_does_not_call_bazaar_or_mutate_order(self):
|
||||
with patch.dict(ScopedRateThrottle.THROTTLE_RATES, {'bazaar_verify': '1/hour'}):
|
||||
with patch('payment.views.bazaar.verify_purchase') as mock_verify:
|
||||
mock_verify.return_value = BazaarPurchaseResult(is_valid=True, purchase_state=0)
|
||||
|
||||
guest1, order1 = self._make_guest_with_order('device-bazaar-guest-rot-4', 'order-bazaar-rot-4')
|
||||
first_response = self._verify(order1.id, 'token-rot-4', issue_access_token(guest1.id))
|
||||
self.assertEqual(first_response.status_code, 200)
|
||||
|
||||
mock_verify.reset_mock()
|
||||
guest2, order2 = self._make_guest_with_order('device-bazaar-guest-rot-5', 'order-bazaar-rot-5')
|
||||
blocked_response = self._verify(order2.id, 'token-rot-5', issue_access_token(guest2.id))
|
||||
self.assertEqual(blocked_response.status_code, 429)
|
||||
mock_verify.assert_not_called()
|
||||
|
||||
order2.refresh_from_db()
|
||||
self.assertEqual(order2.status, 'pending')
|
||||
self.assertEqual(order2.bazaar_purchase_token, '')
|
||||
self.assertFalse(Transaction.objects.filter(order=order2).exists())
|
||||
|
||||
|
||||
class ZarinpalClientTests(TestCase):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user