Improvements in the throttling code
parent
8dfcbe4006
commit
8df7df763c
|
@ -18,6 +18,9 @@
|
||||||
|
|
||||||
from taiga.base.api import throttling
|
from taiga.base.api import throttling
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from ipware.ip import get_ip
|
||||||
|
from netaddr import all_matching_cidrs
|
||||||
|
from netaddr.core import AddrFormatError
|
||||||
|
|
||||||
|
|
||||||
class GlobalThrottlingMixin:
|
class GlobalThrottlingMixin:
|
||||||
|
@ -26,9 +29,7 @@ class GlobalThrottlingMixin:
|
||||||
logged in or not.
|
logged in or not.
|
||||||
"""
|
"""
|
||||||
def get_cache_key(self, request, view):
|
def get_cache_key(self, request, view):
|
||||||
ident = request.META.get("HTTP_X_FORWARDED_FOR")
|
ident = get_ip(request)
|
||||||
if ident is None:
|
|
||||||
ident = request.META.get("REMOTE_ADDR")
|
|
||||||
|
|
||||||
return self.cache_format % {
|
return self.cache_format % {
|
||||||
"scope": self.scope,
|
"scope": self.scope,
|
||||||
|
@ -67,11 +68,24 @@ class CommonThrottle(throttling.SimpleRateThrottle):
|
||||||
def has_to_finalize(self, request, response, view):
|
def has_to_finalize(self, request, response, view):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def is_whitelisted(self, ident):
|
||||||
|
for whitelisted in settings.REST_FRAMEWORK['DEFAULT_THROTTLE_WHITELIST']:
|
||||||
|
if isinstance(whitelisted, int) and whitelisted == ident:
|
||||||
|
return True
|
||||||
|
elif isinstance(whitelisted, str):
|
||||||
|
try:
|
||||||
|
if all_matching_cidrs(ident, [whitelisted]) != []:
|
||||||
|
return True
|
||||||
|
except(AddrFormatError, ValueError):
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
def allow_request(self, request, view):
|
def allow_request(self, request, view):
|
||||||
scope = self.get_scope(request)
|
scope = self.get_scope(request)
|
||||||
ident = self.get_ident(request)
|
ident = self.get_ident(request)
|
||||||
rates = self.get_rates(scope)
|
rates = self.get_rates(scope)
|
||||||
if ident in settings.REST_FRAMEWORK['DEFAULT_THROTTLE_WHITELIST']:
|
|
||||||
|
if self.is_whitelisted(ident):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if rates is None or rates == []:
|
if rates is None or rates == []:
|
||||||
|
@ -145,25 +159,15 @@ class CommonThrottle(throttling.SimpleRateThrottle):
|
||||||
return (rate, num_requests, duration)
|
return (rate, num_requests, duration)
|
||||||
|
|
||||||
def get_scope(self, request):
|
def get_scope(self, request):
|
||||||
if request.user.is_authenticated():
|
scope_prefix = "user" if request.user.is_authenticated() else "anon"
|
||||||
if request.method in ["POST", "PUT", "PATCH", "DELETE"]:
|
scope_sufix = "write" if request.method in ["POST", "PUT", "PATCH", "DELETE"] else "read"
|
||||||
scope = "user-write"
|
scope = "{}-{}".format(scope_prefix, scope_sufix)
|
||||||
else:
|
|
||||||
scope = "user-read"
|
|
||||||
else:
|
|
||||||
if request.method in ["POST", "PUT", "PATCH", "DELETE"]:
|
|
||||||
scope = "anon-write"
|
|
||||||
else:
|
|
||||||
scope = "anon-read"
|
|
||||||
return scope
|
return scope
|
||||||
|
|
||||||
def get_ident(self, request):
|
def get_ident(self, request):
|
||||||
if request.user.is_authenticated():
|
if request.user.is_authenticated():
|
||||||
ident = request.user.id
|
return request.user.id
|
||||||
else:
|
ident = get_ip(request)
|
||||||
ident = request.META.get("HTTP_X_FORWARDED_FOR")
|
|
||||||
if ident is None:
|
|
||||||
ident = request.META.get("REMOTE_ADDR")
|
|
||||||
return ident
|
return ident
|
||||||
|
|
||||||
def get_cache_key(self, ident, scope, rate):
|
def get_cache_key(self, ident, scope, rate):
|
||||||
|
|
|
@ -263,3 +263,33 @@ def test_not_whitelisted_anon_throttling(settings, rf):
|
||||||
cache.clear()
|
cache.clear()
|
||||||
settings.REST_FRAMEWORK['DEFAULT_THROTTLE_RATES']['anon-read'] = None
|
settings.REST_FRAMEWORK['DEFAULT_THROTTLE_RATES']['anon-read'] = None
|
||||||
settings.REST_FRAMEWORK['DEFAULT_THROTTLE_WHITELIST'] = []
|
settings.REST_FRAMEWORK['DEFAULT_THROTTLE_WHITELIST'] = []
|
||||||
|
|
||||||
|
def test_whitelisted_subnet_anon_throttling(settings, rf):
|
||||||
|
settings.REST_FRAMEWORK['DEFAULT_THROTTLE_RATES']['anon-read'] = "1/min"
|
||||||
|
settings.REST_FRAMEWORK['DEFAULT_THROTTLE_WHITELIST'] = ["192.168.0.0/24"]
|
||||||
|
request = rf.get("/test")
|
||||||
|
request.user = AnonymousUser()
|
||||||
|
request.META["REMOTE_ADDR"] = "192.168.0.123"
|
||||||
|
throttling = CommonThrottle()
|
||||||
|
assert throttling.allow_request(request, None)
|
||||||
|
assert throttling.allow_request(request, None)
|
||||||
|
for x in range(100):
|
||||||
|
assert throttling.allow_request(request, None)
|
||||||
|
cache.clear()
|
||||||
|
settings.REST_FRAMEWORK['DEFAULT_THROTTLE_RATES']['anon-read'] = None
|
||||||
|
settings.REST_FRAMEWORK['DEFAULT_THROTTLE_WHITELIST'] = []
|
||||||
|
|
||||||
|
def test_not_whitelisted_subnet_anon_throttling(settings, rf):
|
||||||
|
settings.REST_FRAMEWORK['DEFAULT_THROTTLE_RATES']['anon-read'] = "1/min"
|
||||||
|
settings.REST_FRAMEWORK['DEFAULT_THROTTLE_WHITELIST'] = ["192.168.0.0/24"]
|
||||||
|
request = rf.get("/test")
|
||||||
|
request.user = AnonymousUser()
|
||||||
|
request.META["REMOTE_ADDR"] = "192.168.1.123"
|
||||||
|
throttling = CommonThrottle()
|
||||||
|
assert throttling.allow_request(request, None)
|
||||||
|
assert throttling.allow_request(request, None) is False
|
||||||
|
for x in range(100):
|
||||||
|
assert throttling.allow_request(request, None) is False
|
||||||
|
cache.clear()
|
||||||
|
settings.REST_FRAMEWORK['DEFAULT_THROTTLE_RATES']['anon-read'] = None
|
||||||
|
settings.REST_FRAMEWORK['DEFAULT_THROTTLE_WHITELIST'] = []
|
||||||
|
|
Loading…
Reference in New Issue