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:
100
support/tests.py
100
support/tests.py
@@ -24,7 +24,7 @@ class BugReportCreateThrottleTests(TestCase):
|
||||
def setUp(self):
|
||||
cache.clear()
|
||||
self.client = APIClient()
|
||||
self.user = User.objects.create(device_id='device-bugreport-throttle-1', is_guest=True, name='')
|
||||
self.user = User.objects.create(phone='09000000060', name='')
|
||||
self.token = issue_access_token(self.user.id)
|
||||
|
||||
def _create(self, i=0, remote_addr=None):
|
||||
@@ -56,7 +56,7 @@ class BugReportCreateThrottleTests(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='+989120000901')
|
||||
registered_user = User.objects.create(name='', phone='+989120000901')
|
||||
registered_token = issue_access_token(registered_user.id)
|
||||
|
||||
def _create_as(i, remote_addr):
|
||||
@@ -97,7 +97,7 @@ class TicketCreateThrottleTests(TestCase):
|
||||
def setUp(self):
|
||||
cache.clear()
|
||||
self.client = APIClient()
|
||||
self.user = User.objects.create(device_id='device-ticket-throttle-1', is_guest=True, name='')
|
||||
self.user = User.objects.create(phone='09000000061', name='')
|
||||
self.token = issue_access_token(self.user.id)
|
||||
|
||||
def _create(self, i=0, remote_addr=None):
|
||||
@@ -129,7 +129,7 @@ class TicketCreateThrottleTests(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='+989120000902')
|
||||
registered_user = User.objects.create(name='', phone='+989120000902')
|
||||
registered_token = issue_access_token(registered_user.id)
|
||||
|
||||
def _create_as(i, remote_addr):
|
||||
@@ -170,7 +170,7 @@ class TicketMessageCreateThrottleTests(TestCase):
|
||||
def setUp(self):
|
||||
cache.clear()
|
||||
self.client = APIClient()
|
||||
self.user = User.objects.create(device_id='device-ticketmsg-throttle-1', is_guest=True, name='')
|
||||
self.user = User.objects.create(phone='09000000062', name='')
|
||||
self.token = issue_access_token(self.user.id)
|
||||
self.ticket = Ticket.objects.create(user_id=self.user.id, subject='موضوع', category='billing')
|
||||
|
||||
@@ -203,7 +203,7 @@ class TicketMessageCreateThrottleTests(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='+989120000903')
|
||||
registered_user = User.objects.create(name='', phone='+989120000903')
|
||||
registered_token = issue_access_token(registered_user.id)
|
||||
registered_ticket = Ticket.objects.create(
|
||||
user_id=registered_user.id, subject='موضوع', category='billing',
|
||||
@@ -238,21 +238,8 @@ class TicketMessageCreateThrottleTests(TestCase):
|
||||
self.assertEqual(TicketMessage.objects.filter(ticket=self.ticket).count(), count_before)
|
||||
|
||||
|
||||
class SupportCreateGuestThrottleBypassTests(TestCase):
|
||||
"""
|
||||
🔒 اثبات فیکسِ امنیتی: قبلاً کلیدِ throttle_scope='support_create'
|
||||
(مشترک بینِ BugReportCreateView، TicketCreateView و
|
||||
TicketMessageCreateView) برای هر کاربرِ احرازهویتشده (مهمان یا نه)
|
||||
user.pk بود. چون GuestSessionView با هر deviceId جدید یک کاربرِ
|
||||
مهمانِ تازه (pk جدید) میسازه، یک مهاجم میتونست با ساختنِ پیدرپیِ
|
||||
حسابهای مهمان، هر بار یک bucket throttle تازه برای این scopeِ
|
||||
مشترک بگیره و سقفِ ۳/ساعتِ ساختِ رکوردهای پشتیبانی (BugReport/
|
||||
Ticket/TicketMessage) رو چند برابر کنه. حالا GuestAwareSupportThrottle
|
||||
برای کاربرهای مهمان کلید رو بر اساسِ IP (همون get_ident که
|
||||
guest_session هم استفاده میکنه) میسازه، پس چرخشِ حساب مهمان دیگه
|
||||
bucket تازه نمیده — این هم روی bucket مشترکِ بینِ هر سه Endpoint و
|
||||
هم روی رفتارِ per-user برای کاربرهای غیرمهمان تست میشه.
|
||||
"""
|
||||
class SupportCreatePerUserThrottleTests(TestCase):
|
||||
"""هر کاربرِ ثبتنامشده سطلِ throttleِ جدای خودش را دارد؛ مصرفِ حسابِ دیگر به او نمیرسد."""
|
||||
|
||||
def setUp(self):
|
||||
cache.clear()
|
||||
@@ -277,84 +264,21 @@ class SupportCreateGuestThrottleBypassTests(TestCase):
|
||||
HTTP_AUTHORIZATION=f'Bearer {token}',
|
||||
)
|
||||
|
||||
def test_rotating_guest_accounts_share_the_same_throttle_bucket(self):
|
||||
# سه حساب مهمانِ متفاوت (شبیهسازیِ چرخشِ حساب برای دورزدنِ throttle)،
|
||||
# همه از همون کلاینت/IP — باید همه یک bucket مشترک رو مصرف کنند،
|
||||
# حتی وقتی از دو Endpointِ متفاوتِ همون scope استفاده میکنن.
|
||||
with patch.dict(ScopedRateThrottle.THROTTLE_RATES, {'support_create': '2/hour'}):
|
||||
guest1 = User.objects.create(device_id='device-support-guest-rot-1', is_guest=True, name='')
|
||||
response1 = self._create_bug_report(issue_access_token(guest1.id), 1)
|
||||
self.assertEqual(response1.status_code, 200)
|
||||
|
||||
guest2 = User.objects.create(device_id='device-support-guest-rot-2', is_guest=True, name='')
|
||||
response2 = self._create_ticket(issue_access_token(guest2.id), 2)
|
||||
self.assertEqual(response2.status_code, 200)
|
||||
|
||||
# سومین حسابِ مهمانِ تازه (pk جدید) باید throttle بشه، چون
|
||||
# bucket بر اساسِ IP بهاشتراک گذاشته شده، نه user.pk — یعنی
|
||||
# سقفِ ۲/ساعت هنوز اعمال میشه، صرفنظر از تعدادِ حسابهای
|
||||
# مهمانِ ساختهشده یا اینکه کدوم Endpoint صدا زده شده.
|
||||
guest3 = User.objects.create(device_id='device-support-guest-rot-3', is_guest=True, name='')
|
||||
response3 = self._create_bug_report(issue_access_token(guest3.id), 3)
|
||||
|
||||
self.assertEqual(response3.status_code, 429)
|
||||
|
||||
def test_rotated_guest_account_cannot_get_a_fresh_bucket_after_limit(self):
|
||||
# حتی وقتی حسابِ مهمانِ جدید به سقف رسیده، یک حسابِ مهمانِ دیگر
|
||||
# (چهارمین pk) هم نباید bucket تازهای بگیره — سقف هنوز به IP
|
||||
# مشترک متصل است.
|
||||
with patch.dict(ScopedRateThrottle.THROTTLE_RATES, {'support_create': '1/hour'}):
|
||||
guest1 = User.objects.create(device_id='device-support-guest-rot-4', is_guest=True, name='')
|
||||
response1 = self._create_bug_report(issue_access_token(guest1.id), 4)
|
||||
self.assertEqual(response1.status_code, 200)
|
||||
|
||||
guest2 = User.objects.create(device_id='device-support-guest-rot-5', is_guest=True, name='')
|
||||
blocked = self._create_bug_report(issue_access_token(guest2.id), 5)
|
||||
self.assertEqual(blocked.status_code, 429)
|
||||
|
||||
guest3 = User.objects.create(device_id='device-support-guest-rot-6', is_guest=True, name='')
|
||||
still_blocked = self._create_bug_report(issue_access_token(guest3.id), 6)
|
||||
|
||||
self.assertEqual(still_blocked.status_code, 429)
|
||||
|
||||
def test_registered_users_keep_independent_per_user_buckets(self):
|
||||
# عدمِ رگرسیون: کاربرهای غیرمهمان (ثبتنامشده با شماره) باید مثلِ
|
||||
# قبل هرکدوم bucket جداگانهی خودشون رو داشته باشن — throttle
|
||||
# برای اونا هنوز بر اساسِ user.pk است، نه IP.
|
||||
with patch.dict(ScopedRateThrottle.THROTTLE_RATES, {'support_create': '1/hour'}):
|
||||
user_a = User.objects.create(is_guest=False, name='', phone='+989120001001')
|
||||
user_a = User.objects.create(name='', phone='+989120001001')
|
||||
response_a = self._create_bug_report(issue_access_token(user_a.id), 'a')
|
||||
self.assertEqual(response_a.status_code, 200)
|
||||
|
||||
user_b = User.objects.create(is_guest=False, name='', phone='+989120001002')
|
||||
user_b = User.objects.create(name='', phone='+989120001002')
|
||||
response_b = self._create_bug_report(issue_access_token(user_b.id), 'b')
|
||||
|
||||
self.assertEqual(response_b.status_code, 200)
|
||||
|
||||
def test_blocked_guest_request_does_not_create_support_record(self):
|
||||
# درخواستِ بلاکشده (از حسابِ مهمانِ چرخشی) نباید هیچ BugReport یا
|
||||
# Ticket جدیدی بسازه — throttle باید *قبل* از رسیدن به منطقِ
|
||||
# ساختِ رکورد رد کنه.
|
||||
with patch.dict(ScopedRateThrottle.THROTTLE_RATES, {'support_create': '1/hour'}):
|
||||
guest1 = User.objects.create(device_id='device-support-guest-rot-7', is_guest=True, name='')
|
||||
first_response = self._create_bug_report(issue_access_token(guest1.id), 7)
|
||||
self.assertEqual(first_response.status_code, 200)
|
||||
|
||||
# حسابِ مهمانِ تازهی دوم باید بلاک بشه (bucket مشترکِ IP از
|
||||
# قبل مصرف شده) و نباید BugReport یا Ticket جدیدی بسازد.
|
||||
guest2 = User.objects.create(device_id='device-support-guest-rot-8', is_guest=True, name='')
|
||||
bug_count_before = BugReport.objects.count()
|
||||
ticket_count_before = Ticket.objects.count()
|
||||
|
||||
blocked_bug_response = self._create_bug_report(issue_access_token(guest2.id), 8)
|
||||
self.assertEqual(blocked_bug_response.status_code, 429)
|
||||
|
||||
blocked_ticket_response = self._create_ticket(issue_access_token(guest2.id), 8)
|
||||
self.assertEqual(blocked_ticket_response.status_code, 429)
|
||||
|
||||
self.assertEqual(BugReport.objects.count(), bug_count_before)
|
||||
self.assertEqual(Ticket.objects.count(), ticket_count_before)
|
||||
|
||||
|
||||
class TicketListTests(TestCase):
|
||||
"""GET /api/support/tickets: فقط تیکتهای خودِ کاربر، تازهترین اول، با شمارِ پاسخِ پشتیبانی."""
|
||||
@@ -362,8 +286,8 @@ class TicketListTests(TestCase):
|
||||
def setUp(self):
|
||||
cache.clear()
|
||||
self.client = APIClient()
|
||||
self.user = User.objects.create(device_id='device-ticket-list-1', is_guest=True, name='')
|
||||
self.other = User.objects.create(device_id='device-ticket-list-2', is_guest=True, name='')
|
||||
self.user = User.objects.create(phone='09000000063', name='')
|
||||
self.other = User.objects.create(phone='09000000064', name='')
|
||||
self.auth = {'HTTP_AUTHORIZATION': f'Bearer {issue_access_token(self.user.id)}'}
|
||||
|
||||
def test_lists_only_own_tickets_with_reply_count(self):
|
||||
|
||||
Reference in New Issue
Block a user