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",
),
"DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",),
"DEFAULT_THROTTLE_RATES": {
"burst_search": "100/min",
"sustained_search": "2000/day",
},
}
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 newsreader.core.pagination import LargeResultSetPagination, ResultSetPagination
from newsreader.core.throttling import BurstSearchThrottle, SustainedSearchThrottle
from newsreader.news.collection.models import CollectionRule
from newsreader.news.collection.serializers import RuleSerializer
from newsreader.news.core.filters import ReadFilter
@ -19,9 +20,12 @@ class ListRuleView(ListAPIView):
queryset = CollectionRule.objects.all()
serializer_class = RuleSerializer
pagination_class = ResultSetPagination
filter_backends = [filters.SearchFilter]
search_fields = ["name", "screen_name", "url"]
throttle_classes = [BurstSearchThrottle, SustainedSearchThrottle]
def get_queryset(self):
user = self.request.user
return self.queryset.filter(user=user).order_by("name", "screen_name")

View file

@ -1,14 +1,17 @@
import json
import time
from datetime import date, datetime, time
from unittest import skip
from datetime import datetime
from urllib.parse import urlencode
from django.core.cache import cache
from django.test import TestCase
from django.urls import reverse
import pytz
from freezegun import freeze_time
from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.tests.factories import (
FeedFactory,
@ -210,9 +213,26 @@ class RuleListViewSearchTestCase(TestCase):
self.assertEqual(response_data["results"][1]["id"], rules["foo"].pk)
self.assertEqual(response_data["results"][2]["id"], rules["FooBar"].pk)
@skip("TODO")
@freeze_time("2020-10-30 14:00")
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):
@ -357,23 +377,17 @@ class NestedRuleListViewTestCase(TestCase):
FeedPostFactory(
title="I'm the first post",
rule=rule,
publication_date=datetime.combine(
date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc
),
publication_date=datetime(2019, 5, 20, 16, 7, 37, tzinfo=pytz.utc),
),
FeedPostFactory(
title="I'm the second post",
rule=rule,
publication_date=datetime.combine(
date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc
),
publication_date=datetime(2019, 7, 20, 18, 7, 37, tzinfo=pytz.utc),
),
FeedPostFactory(
title="I'm the third post",
rule=rule,
publication_date=datetime.combine(
date(2019, 7, 20), time(hour=16, minute=7, second=37), pytz.utc
),
publication_date=datetime(2019, 7, 20, 16, 7, 37, tzinfo=pytz.utc),
),
]