Apply ratelimitting to search endpoint

This commit is contained in:
Sonny Bakker 2020-10-17 21:24:44 +02:00
parent b016230500
commit a8ca688456
4 changed files with 55 additions and 13 deletions

View file

@ -228,6 +228,10 @@ REST_FRAMEWORK = {
"newsreader.accounts.permissions.IsOwner", "newsreader.accounts.permissions.IsOwner",
), ),
"DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",), "DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",),
"DEFAULT_THROTTLE_RATES": {
"burst_search": "100/min",
"sustained_search": "2000/day",
},
} }
SWAGGER_SETTINGS = { SWAGGER_SETTINGS = {

View file

@ -0,0 +1,20 @@
from rest_framework.throttling import UserRateThrottle
class SearchThrottle(UserRateThrottle):
"""
Only applies throttling to requests with the search param
"""
def allow_request(self, request, view):
if not "search" in request.GET.keys():
return True
return super().allow_request(request, view)
class BurstSearchThrottle(SearchThrottle):
scope = "burst_search"
class SustainedSearchThrottle(SearchThrottle):
scope = "sustained_search"

View file

@ -8,6 +8,7 @@ from rest_framework.generics import (
from rest_framework.response import Response from rest_framework.response import Response
from newsreader.core.pagination import LargeResultSetPagination, ResultSetPagination from newsreader.core.pagination import LargeResultSetPagination, ResultSetPagination
from newsreader.core.throttling import BurstSearchThrottle, SustainedSearchThrottle
from newsreader.news.collection.models import CollectionRule from newsreader.news.collection.models import CollectionRule
from newsreader.news.collection.serializers import RuleSerializer from newsreader.news.collection.serializers import RuleSerializer
from newsreader.news.core.filters import ReadFilter from newsreader.news.core.filters import ReadFilter
@ -19,9 +20,12 @@ class ListRuleView(ListAPIView):
queryset = CollectionRule.objects.all() queryset = CollectionRule.objects.all()
serializer_class = RuleSerializer serializer_class = RuleSerializer
pagination_class = ResultSetPagination pagination_class = ResultSetPagination
filter_backends = [filters.SearchFilter] filter_backends = [filters.SearchFilter]
search_fields = ["name", "screen_name", "url"] search_fields = ["name", "screen_name", "url"]
throttle_classes = [BurstSearchThrottle, SustainedSearchThrottle]
def get_queryset(self): def get_queryset(self):
user = self.request.user user = self.request.user
return self.queryset.filter(user=user).order_by("name", "screen_name") return self.queryset.filter(user=user).order_by("name", "screen_name")

View file

@ -1,14 +1,17 @@
import json import json
import time
from datetime import date, datetime, time from datetime import datetime
from unittest import skip
from urllib.parse import urlencode from urllib.parse import urlencode
from django.core.cache import cache
from django.test import TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
import pytz import pytz
from freezegun import freeze_time
from newsreader.accounts.tests.factories import UserFactory from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.tests.factories import ( from newsreader.news.collection.tests.factories import (
FeedFactory, FeedFactory,
@ -210,9 +213,26 @@ class RuleListViewSearchTestCase(TestCase):
self.assertEqual(response_data["results"][1]["id"], rules["foo"].pk) self.assertEqual(response_data["results"][1]["id"], rules["foo"].pk)
self.assertEqual(response_data["results"][2]["id"], rules["FooBar"].pk) self.assertEqual(response_data["results"][2]["id"], rules["FooBar"].pk)
@skip("TODO") @freeze_time("2020-10-30 14:00")
def test_ratelimitting(self): def test_ratelimitting(self):
pass # Trigger ratelimit
cache.set(
f"throttle_burst_search_{self.user.pk}", [time.time() for i in range(100)]
)
params = urlencode({"search": "foo"})
url = reverse("api:news:collection:rules-list")
response = self.client.get(f"{url}?{params}")
response_data = response.json()
self.assertEqual(response.status_code, 429)
message = response_data["detail"]
self.assertIn("Request was throttled", message)
cache.delete(f"throttle_burst_search_{self.user.pk}")
class NestedRuleListViewTestCase(TestCase): class NestedRuleListViewTestCase(TestCase):
@ -357,23 +377,17 @@ class NestedRuleListViewTestCase(TestCase):
FeedPostFactory( FeedPostFactory(
title="I'm the first post", title="I'm the first post",
rule=rule, rule=rule,
publication_date=datetime.combine( publication_date=datetime(2019, 5, 20, 16, 7, 37, tzinfo=pytz.utc),
date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc
),
), ),
FeedPostFactory( FeedPostFactory(
title="I'm the second post", title="I'm the second post",
rule=rule, rule=rule,
publication_date=datetime.combine( publication_date=datetime(2019, 7, 20, 18, 7, 37, tzinfo=pytz.utc),
date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc
),
), ),
FeedPostFactory( FeedPostFactory(
title="I'm the third post", title="I'm the third post",
rule=rule, rule=rule,
publication_date=datetime.combine( publication_date=datetime(2019, 7, 20, 16, 7, 37, tzinfo=pytz.utc),
date(2019, 7, 20), time(hour=16, minute=7, second=37), pytz.utc
),
), ),
] ]