def test_default_rate_limit_values(self): """Ensure that the default rate limits are called for endpoints without overrides.""" class TestEndpoint(Endpoint): pass assert get_rate_limit_value( "GET", TestEndpoint, RateLimitCategory.IP ) == get_default_rate_limits_for_group("default", RateLimitCategory.IP) assert get_rate_limit_value( "POST", TestEndpoint, RateLimitCategory.ORGANIZATION ) == get_default_rate_limits_for_group("default", RateLimitCategory.ORGANIZATION) assert get_rate_limit_value( "DELETE", TestEndpoint, RateLimitCategory.USER ) == get_default_rate_limits_for_group("default", RateLimitCategory.USER)
def test_multiple_inheritance(self): class ParentEndpoint(Endpoint): rate_limits = {"GET": {RateLimitCategory.IP: RateLimit(100, 5)}} class Mixin: rate_limits = {"GET": {RateLimitCategory.IP: RateLimit(2, 4)}} class ChildEndpoint(ParentEndpoint, Mixin): pass class ChildEndpointReverse(Mixin, ParentEndpoint): pass assert get_rate_limit_value("GET", ChildEndpoint, RateLimitCategory.IP) == RateLimit(100, 5) assert get_rate_limit_value("GET", ChildEndpointReverse, RateLimitCategory.IP) == RateLimit( 2, 4 )
def test_override_rate_limit(self): """Override one or more of the default rate limits.""" class TestEndpoint(Endpoint): rate_limits = { "GET": {RateLimitCategory.IP: RateLimit(100, 5)}, "POST": {RateLimitCategory.USER: RateLimit(20, 4)}, } assert get_rate_limit_value("GET", TestEndpoint, RateLimitCategory.IP) == RateLimit(100, 5) assert get_rate_limit_value( "GET", TestEndpoint, RateLimitCategory.USER ) == get_default_rate_limits_for_group("default", RateLimitCategory.USER) assert get_rate_limit_value( "POST", TestEndpoint, RateLimitCategory.IP ) == get_default_rate_limits_for_group("default", RateLimitCategory.IP) assert get_rate_limit_value("POST", TestEndpoint, RateLimitCategory.USER) == RateLimit( 20, 4 )
def test_inherit(self): class ParentEndpoint(Endpoint): rate_limits = RateLimitConfig( group="foo", limit_overrides={"GET": {RateLimitCategory.IP: RateLimit(100, 5)}} ) class ChildEndpoint(ParentEndpoint): rate_limits = RateLimitConfig(group="foo", limit_overrides={"GET": {}}) assert get_rate_limit_value( "GET", ChildEndpoint, RateLimitCategory.IP ) == get_default_rate_limits_for_group("foo", RateLimitCategory.IP)
def process_view(self, request: Request, view_func, view_args, view_kwargs) -> Response | None: """Check if the endpoint call will violate.""" try: # TODO: put these fields into their own object request.will_be_rate_limited = False request.rate_limit_category = None request.rate_limit_uid = uuid.uuid4().hex request.rate_limit_key = get_rate_limit_key(view_func, request) if request.rate_limit_key is None: return category_str = request.rate_limit_key.split(":", 1)[0] request.rate_limit_category = category_str rate_limit = get_rate_limit_value( http_method=request.method, endpoint=view_func.view_class, category=RateLimitCategory(category_str), ) if rate_limit is None: return request.rate_limit_metadata = above_rate_limit_check( request.rate_limit_key, rate_limit, request.rate_limit_uid) # TODO: also limit by concurrent window once we have the data rate_limit_cond = (request.rate_limit_metadata.rate_limit_type != RateLimitType.NOT_LIMITED if ENFORCE_CONCURRENT_RATE_LIMITS else request.rate_limit_metadata.rate_limit_type == RateLimitType.FIXED_WINDOW) if rate_limit_cond: request.will_be_rate_limited = True enforce_rate_limit = getattr(view_func.view_class, "enforce_rate_limit", False) if enforce_rate_limit: return HttpResponse( { "detail": DEFAULT_ERROR_MESSAGE.format( limit=request.rate_limit_metadata.limit, window=request.rate_limit_metadata.window, ) }, status=429, ) except Exception: logging.exception( "Error during rate limiting, failing open. THIS SHOULD NOT HAPPEN" )
def process_view(self, request: Request, view_func, view_args, view_kwargs) -> Response | None: """Check if the endpoint call will violate.""" request.will_be_rate_limited = False request.rate_limit_category = None key = get_rate_limit_key(view_func, request) if key is None: return category_str = key.split(":", 1)[0] request.rate_limit_category = category_str rate_limit = get_rate_limit_value( http_method=request.method, endpoint=view_func.view_class, category=RateLimitCategory(category_str), ) if rate_limit is None: return request.rate_limit_metadata = above_rate_limit_check(key, rate_limit) if request.rate_limit_metadata.is_limited: request.will_be_rate_limited = True enforce_rate_limit = getattr(view_func.view_class, "enforce_rate_limit", False) if enforce_rate_limit: return HttpResponse( { "detail": DEFAULT_ERROR_MESSAGE.format( limit=request.rate_limit_metadata.limit, window=request.rate_limit_metadata.window, ) }, status=429, )