throttling, abuse-protection & logging/monitoring pass
This commit is contained in:
@@ -9,6 +9,7 @@ from drf_spectacular.utils import extend_schema
|
||||
from rest_framework.exceptions import NotFound, PermissionDenied
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.throttling import ScopedRateThrottle
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from .models import FamilyLink, ParentalSettings, WeeklyReportLog
|
||||
@@ -30,11 +31,56 @@ def _week_start(dt):
|
||||
return (dt - timedelta(days=dt.weekday())).date()
|
||||
|
||||
|
||||
class GuestAwareFamilyThrottle(ScopedRateThrottle):
|
||||
"""
|
||||
⚠️ اضافه شده: همون مشکلِ GuestAwareAIThrottle (ai_tutor/views.py) اینجا
|
||||
هم هست. ScopedRateThrottle استاندارد برای درخواستِ احرازهویتشده از
|
||||
request.user.pk بهعنوانِ کلیدِ throttle استفاده میکنه؛ چون
|
||||
GuestSessionView با هر deviceId جدید یک کاربرِ مهمانِ تازه (pk جدید)
|
||||
میسازه، یک مهاجم میتونست با ساختنِ پیدرپیِ حسابهای مهمان (تا سقفِ
|
||||
guest_session=20/hour)، برای هر حساب یک bucket تازه از throttle_scope
|
||||
'family_invite_accept' بگیره و عملاً محدودیتِ ۱۰/ساعتِ brute-force
|
||||
رویِ inviteCode ۶رقمی رو دور بزنه.
|
||||
|
||||
دقیقاً طبقِ همون الگو: فقط برای کاربرهای مهمان (is_guest=True) کلیدِ
|
||||
cache از pk کاربر به IP کلاینت (get_ident) تغییر میکنه، پس همهی
|
||||
حسابهای مهمانِ ساختهشده از یک IP یک bucket مشترک دارن و چرخشِ حساب
|
||||
دیگه bucket تازه نمیده. رفتارِ throttle برای کاربرهای غیرمهمان
|
||||
(ثبتنامشده) دقیقاً مثلِ قبل (کلید بر اساسِ pk) باقی میمونه؛ منطقِ
|
||||
generation/authorization/notify اکسپت دستنخوردهست.
|
||||
"""
|
||||
|
||||
def get_cache_key(self, request, view):
|
||||
user = request.user
|
||||
if user and user.is_authenticated and getattr(user, 'is_guest', False):
|
||||
ident = f'guest-ip:{self.get_ident(request)}'
|
||||
elif user and user.is_authenticated:
|
||||
ident = user.pk
|
||||
else:
|
||||
ident = self.get_ident(request)
|
||||
|
||||
return self.cache_format % {'scope': self.scope, 'ident': ident}
|
||||
|
||||
|
||||
class FamilyLinkInviteView(APIView):
|
||||
"""POST /api/family/link/invite — دعوت فرزند توسط والد (تولید کد اتصال)"""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
# ⚠️ اضافه شده: این endpoint قبلاً هیچ throttle مخصوصی نداشت، درحالیکه
|
||||
# یک FamilyLink ماندگار میسازه و یک کدِ دعوتِ ۶رقمیِ تازه تولید میکنه.
|
||||
# بدونِ throttle، یک کاربرِ احرازهویتشده میتونست با فرستادنِ
|
||||
# پیدرپیِ شمارههای مختلف، تعدادِ زیادی FamilyLink/کدِ دعوت بسازه.
|
||||
# طبقِ همون الگویِ GuestAwareFamilyThrottleِ مشترک (که
|
||||
# FamilyLinkAcceptView هم استفاده میکنه): برای کاربرهای مهمان کلید
|
||||
# بر اساسِ IP و برای کاربرهای غیرمهمان بر اساسِ user.pk است، پس
|
||||
# چرخشِ حسابِ مهمان نمیتونه bucket تازه بگیره. یک throttle_scope
|
||||
# مجزا ('family_invite_create'، نه 'family_invite_accept') استفاده
|
||||
# شده چون تولید و مصرفِ کدِ دعوت باید مستقل از هم قابلتنظیم بمونن؛
|
||||
# منطقِ generation/authorization دستنخوردهست.
|
||||
throttle_scope = 'family_invite_create'
|
||||
throttle_classes = [GuestAwareFamilyThrottle]
|
||||
|
||||
@extend_schema(
|
||||
request=FamilyLinkInviteRequestSerializer,
|
||||
responses=FamilyLinkInviteResponseSerializer,
|
||||
@@ -85,6 +131,19 @@ class FamilyLinkAcceptView(APIView):
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
# ⚠️ اضافه شده: inviteCode یک رازِ عددیِ ۶رقمی تولیدشدهی سرور است
|
||||
# (family/views.py::FamilyLinkInviteView)، و این endpoint دقیقاً همون
|
||||
# کد رو با select_for_update() جستوجو میکنه؛ بدونِ throttle، یک
|
||||
# کاربرِ احرازهویتشده میتونست بدونِ هیچ محدودیتی فضای ۶رقمی رو
|
||||
# brute-force کنه. طبقِ همون الگویِ ScopedRateThrottle که برای بقیهی
|
||||
# اندپوینتهایِ حدسپذیر پروژه اضافه شد، اینجا هم یک scope مجزا اضافه
|
||||
# شد (نه scope مشترک با discount/offer) تا نرخِ محدودسازی مستقل از اونها
|
||||
# قابلتنظیم باشه. چون DRF throttle check قبل از dispatch به post() در
|
||||
# APIView.initial() اجرا میشه، حدسهای بلاکشده اصلاً به select_for_update()
|
||||
# نمیرسن.
|
||||
throttle_scope = 'family_invite_accept'
|
||||
throttle_classes = [GuestAwareFamilyThrottle]
|
||||
|
||||
@extend_schema(
|
||||
request=FamilyLinkAcceptRequestSerializer,
|
||||
responses=FamilyLinkAcceptResponseSerializer,
|
||||
|
||||
Reference in New Issue
Block a user