Skip to content

Commit 6867cd5

Browse files
committed
Read scoped throttle rates dynamically
1 parent 0a05469 commit 6867cd5

2 files changed

Lines changed: 58 additions & 1 deletion

File tree

core/throttling.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
from rest_framework.throttling import ScopedRateThrottle
2+
from rest_framework.settings import api_settings
23

34

45
class PostOnlyScopedRateThrottle(ScopedRateThrottle):
6+
def get_rate(self):
7+
if not getattr(self, "scope", None):
8+
return None
9+
return api_settings.DEFAULT_THROTTLE_RATES.get(self.scope)
10+
511
def allow_request(self, request, view):
612
if request.method != "POST":
713
return True

users/tests/test_auth_throttling.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
from django.conf import settings
44
from django.core.cache import cache
55
from django.test import TestCase, override_settings
6-
from rest_framework.test import APIClient
6+
from rest_framework.response import Response
7+
from rest_framework.test import APIClient, APIRequestFactory
8+
from rest_framework.views import APIView
79

10+
from core.throttling import PostOnlyScopedRateThrottle
811
from tests.constants import USER_CREATE_DATA
912
from users.tests.helpers import build_user
1013

@@ -18,10 +21,22 @@ def throttle_settings(**rates):
1821
return rest_framework
1922

2023

24+
class DummyPostOnlyThrottleView(APIView):
25+
throttle_classes = [PostOnlyScopedRateThrottle]
26+
throttle_scope = "auth_register"
27+
28+
def get(self, _request):
29+
return Response({"ok": True})
30+
31+
def post(self, _request):
32+
return Response({"ok": True})
33+
34+
2135
class AuthThrottleTests(TestCase):
2236
def setUp(self):
2337
cache.clear()
2438
self.client = APIClient()
39+
self.factory = APIRequestFactory()
2540

2641
@override_settings(
2742
REST_FRAMEWORK=throttle_settings(auth_register="1/min")
@@ -118,3 +133,39 @@ def test_password_reset_request_post_is_scoped_throttled(self, _email_message):
118133

119134
self.assertNotEqual(first_response.status_code, 429)
120135
self.assertEqual(second_response.status_code, 429)
136+
137+
@override_settings(
138+
REST_FRAMEWORK=throttle_settings(auth_register="1/min")
139+
)
140+
def test_post_only_throttle_allows_get_and_options(self):
141+
view = DummyPostOnlyThrottleView.as_view()
142+
143+
for method in ("get", "options"):
144+
for _ in range(2):
145+
response = view(
146+
getattr(self.factory, method)(
147+
"/unused/",
148+
REMOTE_ADDR="203.0.113.14",
149+
)
150+
)
151+
self.assertNotEqual(response.status_code, 429)
152+
153+
first_response = view(
154+
self.factory.post(
155+
"/unused/",
156+
{},
157+
format="json",
158+
REMOTE_ADDR="203.0.113.14",
159+
)
160+
)
161+
second_response = view(
162+
self.factory.post(
163+
"/unused/",
164+
{},
165+
format="json",
166+
REMOTE_ADDR="203.0.113.14",
167+
)
168+
)
169+
170+
self.assertEqual(first_response.status_code, 200)
171+
self.assertEqual(second_response.status_code, 429)

0 commit comments

Comments
 (0)