feat: added configurable param to rate limit email change in account settings (#37349)

This commit is contained in:
Muhammad Adeel Tajamul
2025-09-12 18:21:10 +05:00
committed by GitHub
parent 2a2942bc7f
commit 1a081f23c0
3 changed files with 36 additions and 0 deletions

View File

@@ -1063,6 +1063,34 @@ class TestAccountsAPI(FilteredQueryCountMixin, CacheIsolationTestCase, UserAPITe
get_response = self.send_get(client)
assert new_email == get_response.data['email']
@override_settings(EMAIL_CHANGE_RATE_LIMIT='1/m')
def test_patch_email_ratelimit(self):
"""
Tests if rate limit is applied on email patch
"""
client = self.login_client("client", "user")
self.send_patch(client, {"email": "new_email_01@example.com"}, expected_status=status.HTTP_200_OK)
self.send_patch(client, {"email": "new_email_02@example.com"},
expected_status=status.HTTP_429_TOO_MANY_REQUESTS)
@override_settings(EMAIL_CHANGE_RATE_LIMIT='')
def test_ratelimit_is_disabled_on_email_patch_if_settings_is_empty(self):
"""
Tests if rate limit doesn't applied on email patch if EMAIL_CHANGE_RATE_LIMIT is empty string or None
"""
client = self.login_client("client", "user")
self.send_patch(client, {"email": "email_new_01@example.com"}, expected_status=status.HTTP_200_OK)
self.send_patch(client, {"email": "email_new_02@example.com"}, expected_status=status.HTTP_200_OK)
@override_settings(EMAIL_CHANGE_RATE_LIMIT='1/d')
def test_ratelimit_is_only_on_email_change(self):
"""
Tests if rate limit is only applied for email attribute i.e. when user changes email
"""
client = self.login_client("client", "user")
for i in range(5):
self.send_patch(client, {"name": f"new_name_{i}"}, expected_status=status.HTTP_200_OK)
@ddt.data(
("not_an_email",),
("",),

View File

@@ -18,6 +18,7 @@ from django.contrib.sites.models import Site
from django.core.cache import cache
from django.db import transaction
from django.utils.translation import gettext as _
from django_ratelimit.core import is_ratelimited
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from edx_ace import ace
@@ -395,6 +396,12 @@ class AccountViewSet(ViewSet):
"""
if request.content_type != MergePatchParser.media_type:
raise UnsupportedMediaType(request.content_type)
if request.data.get("email") and settings.EMAIL_CHANGE_RATE_LIMIT:
if is_ratelimited(
request=request, group="email_change_rate_limit", key="user",
rate=settings.EMAIL_CHANGE_RATE_LIMIT, increment=True,
):
return Response({"error": "Too many requests"}, status=status.HTTP_429_TOO_MANY_REQUESTS)
try:
with transaction.atomic():

View File

@@ -829,6 +829,7 @@ DISCUSSION_RATELIMIT = '100/m'
SKIP_RATE_LIMIT_ON_ACCOUNT_AFTER_DAYS = 0
ONE_CLICK_UNSUBSCRIBE_RATE_LIMIT = '100/m'
EMAIL_CHANGE_RATE_LIMIT = ''
LMS_ROOT_URL = None
LMS_INTERNAL_ROOT_URL = Derived(lambda settings: settings.LMS_ROOT_URL)