def test_request_delete_limit_email(_set_up_client, _reset_limits, _clear_db): client = app.test_client() limit = int(app.config['CREATE_EMAIL_RATE_LIMIT'].split('/')[0]) ip_limit = int(app.config['IP_RATE_LIMIT'].split('/')[0]) # Must hit email limit first assert limit < ip_limit response = create_email_alias() data = json.loads(response.data) email = data['email'] assert response.status_code == status.HTTP_201_CREATED # Valid requests for _ in range(limit): data = dict(email=email, ) data = json.dumps(data) response = client.post('/api/request_delete', data=data, content_type='application/json') assert response.status_code == status.HTTP_200_OK # Invalid request data = dict(email=email, ) data = json.dumps(data) response = client.post('/api/request_delete', data=data, content_type='application/json') assert response.status_code == status.HTTP_429_TOO_MANY_REQUESTS assert 'Exceeded limit for same email address' in str(response.data) limit = str(limits.parse(app.config['REQUEST_DELETE_RATE_LIMIT'])) assert limit in str(response.data)
def test_create_rate_limit_ip(_set_up_client, _reset_limits, _clear_db): limit = int(app.config['IP_RATE_LIMIT'].split('/')[0]) # Need to extend possibilities to hit rate limit app.config['BEAUTIFURL_FORMAT'] = 'www' for i in range(limit): response = create_email_alias(prefix=i) assert response.status_code == status.HTTP_201_CREATED response = create_email_alias() assert response.status_code == status.HTTP_429_TOO_MANY_REQUESTS assert 'Exceeded limit from same ip address' in str(response.data) limit = str(limits.parse(app.config['IP_RATE_LIMIT'])) assert limit in str(response.data)
def limiter(frequency): frequency = parse(frequency) def decorator(function): def wrapper(request, *args, **kwargs): namespace = f"{function.__module__}.{function.__name__}" ip = request.ip[0] or '127.0.0.1' ip = request.headers.get("X-Forwarded-For", ip) if not _MOVING_WINDOW.hit(frequency, namespace, ip): raise TooManyRequests(_LIMIT_MESSAGE) return function(request, *args, **kwargs) return wrapper return decorator
def test_request_delete_rate_limit_ip(_set_up_client, _reset_limits, _clear_db): client = app.test_client() limit = int(app.config['IP_RATE_LIMIT'].split('/')[0]) for i in range(limit): # Ensure each email unique to avoid email rate limit # Binary just for fun data = dict(email='{0:b}@shadowmail.co.uk'.format(i), ) data = json.dumps(data) response = client.post('/api/request_delete', data=data, content_type='application/json') assert response.status_code == status.HTTP_400_BAD_REQUEST assert 'Email address not found' in str(response.data) data = dict(email='{0:b}@shadowmail.co.uk'.format(i + 1), ) data = json.dumps(data) response = client.post('/api/request_delete', data=data, content_type='application/json') assert response.status_code == status.HTTP_429_TOO_MANY_REQUESTS assert 'Exceeded limit from same ip address' in str(response.data) limit = str(limits.parse(app.config['IP_RATE_LIMIT'])) assert limit in str(response.data)
def get_limiter(self, limit, *args): return LimitWrapper(self.limiter, limits.parse(limit), *args)
EMAIL_REGEX = '.+@.+' @email_limiter.request_filter def no_email_whitelist(): data = request.get_json() return data is None or 'email' not in data def get_email_from_request(): data = request.get_json(force=True) return data['email'] IP_ERROR_MESSAGE = 'Exceeded limit from same ip address: {}'.format( limits.parse(app.config['IP_RATE_LIMIT'])) CREATE_EMAIL_ERROR_MESSAGE = 'Exceeded limit for same email address: {}'.format( limits.parse(app.config['CREATE_EMAIL_RATE_LIMIT'])) REQUEST_DELETE_ERROR_MESSAGE = 'Exceeded limit for same email address: {}'.format( limits.parse(app.config['REQUEST_DELETE_RATE_LIMIT'])) @app.route('/api/new', methods=['POST']) @ip_limiter.limit(app.config['IP_RATE_LIMIT'], get_remote_address, error_message=IP_ERROR_MESSAGE) @email_limiter.limit(app.config['CREATE_EMAIL_RATE_LIMIT'], get_email_from_request, error_message=CREATE_EMAIL_ERROR_MESSAGE)
def init_app(self, app): self.storage = limits.storage.storage_from_string(app.config["RATELIMIT_STORAGE_URL"]) self.limiter = limits.strategies.MovingWindowRateLimiter(self.storage) self.rate = limits.parse(app.config["AUTH_RATELIMIT"]) self.rate_limit_subnet = str(app.config["AUTH_RATELIMIT_SUBNET"])!='False' self.subnet = ipaddress.ip_network(app.config["SUBNET"])