misc: add rate limiting to tracking code URL (#35386)
This commit is contained in:
parent
b7cbff134c
commit
32f304fd51
|
@ -15,6 +15,7 @@ Depends: ${misc:Depends}, ${python:Depends},
|
|||
python-hobo,
|
||||
graphviz,
|
||||
python-django-ckeditor,
|
||||
python-django-ratelimit,
|
||||
python-feedparser,
|
||||
python-imaging,
|
||||
python-pyproj,
|
||||
|
|
|
@ -27,7 +27,7 @@ $PIP_BIN install --upgrade setuptools
|
|||
$PIP_BIN install --upgrade 'pytest<4.1' WebTest mock pytest-cov pyquery pytest-django
|
||||
$PIP_BIN install --upgrade 'pylint<1.8' # 1.8 broken (cf build #3023)
|
||||
$PIP_BIN install git+https://git.entrouvert.org/debian/django-ckeditor.git
|
||||
$PIP_BIN install --upgrade 'Django<1.12' 'gadjo' 'pyproj'
|
||||
$PIP_BIN install --upgrade 'Django<1.12' 'gadjo' 'pyproj' 'django-ratelimit<3'
|
||||
|
||||
DJANGO_SETTINGS_MODULE=wcs.settings \
|
||||
WCS_SETTINGS_FILE=tests/settings.py \
|
||||
|
|
1
setup.py
1
setup.py
|
@ -109,6 +109,7 @@ setup(
|
|||
install_requires=[
|
||||
'gadjo>=0.53',
|
||||
'django-ckeditor<=4.5.3',
|
||||
'django-ratelimit<3',
|
||||
'XStatic-Leaflet',
|
||||
'pyproj',
|
||||
],
|
||||
|
|
|
@ -68,3 +68,12 @@ def sms_mocking():
|
|||
def http_requests():
|
||||
with HttpRequestsMocking() as http_requests:
|
||||
yield http_requests
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def nocache(settings):
|
||||
settings.CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,6 @@ def assert_equal_zip(stream1, stream2):
|
|||
t1, t2 = z1.read(name), z2.read(name)
|
||||
assert t1 == t2, 'file "%s" differs' % name
|
||||
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
if 'pub' in metafunc.fixturenames:
|
||||
metafunc.parametrize('pub', ['pickle', 'sql', 'pickle-templates', 'pickle-lazy'], indirect=True)
|
||||
|
@ -1311,7 +1310,7 @@ def get_displayed_tracking_code(resp):
|
|||
break
|
||||
return tracking_code
|
||||
|
||||
def test_form_tracking_code(pub):
|
||||
def test_form_tracking_code(pub, nocache):
|
||||
formdef = create_formdef()
|
||||
formdef.fields = [fields.StringField(id='0', label='string')]
|
||||
formdef.enable_tracking_codes = True
|
||||
|
@ -1412,8 +1411,16 @@ def test_form_tracking_code(pub):
|
|||
assert resp.location == 'http://example.net/test/%s' % formdata_id
|
||||
resp = resp.follow()
|
||||
|
||||
def test_form_tracking_code_rate_limit(pub):
|
||||
# three errors
|
||||
get_app(pub).get('/code/ABC/load', status=404)
|
||||
get_app(pub).get('/code/ABC/load', status=404)
|
||||
get_app(pub).get('/code/ABC/load', status=404)
|
||||
# and out
|
||||
get_app(pub).get('/code/ABC/load', status=403)
|
||||
get_app(pub).get('/code/ABC/load', status=403)
|
||||
|
||||
def test_form_tracking_code_as_user(pub):
|
||||
def test_form_tracking_code_as_user(pub, nocache):
|
||||
user = create_user(pub)
|
||||
formdef = create_formdef()
|
||||
formdef.fields = [fields.StringField(id='0', label='string')]
|
||||
|
@ -1493,7 +1500,7 @@ def test_form_tracking_code_as_user(pub):
|
|||
resp = app.get('/code/%s/load' % tracking_code,
|
||||
headers={'User-agent': 'Googlebot'}, status=403)
|
||||
|
||||
def test_form_empty_tracking_code(pub):
|
||||
def test_form_empty_tracking_code(pub, nocache):
|
||||
formdef = create_formdef()
|
||||
formdef.fields = [fields.StringField(id='0', label='string')]
|
||||
formdef.enable_tracking_codes = True
|
||||
|
@ -1512,7 +1519,7 @@ def test_form_empty_tracking_code(pub):
|
|||
assert resp.location == 'http://example.net/code/%s/load' % tracking_code
|
||||
resp = resp.follow(status=404)
|
||||
|
||||
def test_form_tracking_code_email(pub, emails):
|
||||
def test_form_tracking_code_email(pub, emails, nocache):
|
||||
formdef = create_formdef()
|
||||
formdef.data_class().wipe()
|
||||
formdef.fields = [fields.StringField(id='0', label='string'),
|
||||
|
@ -1541,7 +1548,7 @@ def test_form_tracking_code_email(pub, emails):
|
|||
resp = resp.follow()
|
||||
assert resp.forms[1]['f0'].value == 'barfoo'
|
||||
|
||||
def test_form_tracking_code_remove_draft(pub):
|
||||
def test_form_tracking_code_remove_draft(pub, nocache):
|
||||
formdef = create_formdef()
|
||||
formdef.fields = [fields.StringField(id='0', label='string')]
|
||||
formdef.enable_tracking_codes = True
|
||||
|
@ -1587,7 +1594,7 @@ def test_form_tracking_code_remove_draft(pub):
|
|||
assert resp.location == 'http://example.net/'
|
||||
assert formdef.data_class().count() == 0
|
||||
|
||||
def test_form_tracking_code_remove_empty_draft(pub):
|
||||
def test_form_tracking_code_remove_empty_draft(pub, nocache):
|
||||
formdef = create_formdef()
|
||||
formdef.fields = [fields.StringField(id='0', label='string')]
|
||||
formdef.enable_tracking_codes = True
|
||||
|
@ -1635,7 +1642,7 @@ def test_form_tracking_code_remove_empty_draft(pub):
|
|||
assert resp.location == 'http://example.net/'
|
||||
assert formdef.data_class().count() == 0
|
||||
|
||||
def test_form_discard_draft(pub):
|
||||
def test_form_discard_draft(pub, nocache):
|
||||
user = create_user(pub)
|
||||
|
||||
formdef = create_formdef()
|
||||
|
@ -1733,7 +1740,7 @@ def test_form_discard_draft(pub):
|
|||
resp = resp.forms[1].submit('cancel')
|
||||
assert [x.status for x in formdef.data_class().select()] == ['draft']
|
||||
|
||||
def test_form_invalid_tracking_code(pub):
|
||||
def test_form_invalid_tracking_code(pub, nocache):
|
||||
formdef = create_formdef()
|
||||
formdef.fields = [fields.StringField(id='0', label='string')]
|
||||
formdef.enable_tracking_codes = True
|
||||
|
@ -1785,7 +1792,7 @@ def test_form_invalid_tracking_code(pub):
|
|||
assert resp.location == 'http://example.net/code/%s/load' % code.id
|
||||
resp = resp.follow(status=404)
|
||||
|
||||
def test_form_tracking_code_as_variable(pub):
|
||||
def test_form_tracking_code_as_variable(pub, nocache):
|
||||
formdef = create_formdef()
|
||||
formdef.fields = [fields.PageField(id='0', label='1st page', type='page'),
|
||||
fields.StringField(id='1', label='string'),
|
||||
|
|
|
@ -29,6 +29,8 @@ from django.utils.http import quote
|
|||
from django.utils.six import StringIO
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
import ratelimit.utils
|
||||
|
||||
from quixote import (get_publisher, get_request, get_response, get_session,
|
||||
get_session_manager, redirect)
|
||||
from quixote.directory import Directory, AccessControlled
|
||||
|
@ -148,6 +150,16 @@ class TrackingCodeDirectory(Directory):
|
|||
return r.getvalue()
|
||||
|
||||
def load(self):
|
||||
rate_limit = get_publisher().get_site_option('rate-limit') or '3/s'
|
||||
if rate_limit != 'none':
|
||||
ratelimited = ratelimit.utils.is_ratelimited(
|
||||
request=get_request().django_request,
|
||||
group='trackingcode',
|
||||
key='ip',
|
||||
rate=rate_limit,
|
||||
increment=True)
|
||||
if ratelimited:
|
||||
raise errors.AccessForbiddenError('rate limit reached')
|
||||
try:
|
||||
tracking_code = get_publisher().tracking_code_class.get(self.code)
|
||||
if tracking_code.formdata_id is None:
|
||||
|
|
Loading…
Reference in New Issue