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,
|
python-hobo,
|
||||||
graphviz,
|
graphviz,
|
||||||
python-django-ckeditor,
|
python-django-ckeditor,
|
||||||
|
python-django-ratelimit,
|
||||||
python-feedparser,
|
python-feedparser,
|
||||||
python-imaging,
|
python-imaging,
|
||||||
python-pyproj,
|
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 '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 --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 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 \
|
DJANGO_SETTINGS_MODULE=wcs.settings \
|
||||||
WCS_SETTINGS_FILE=tests/settings.py \
|
WCS_SETTINGS_FILE=tests/settings.py \
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -109,6 +109,7 @@ setup(
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'gadjo>=0.53',
|
'gadjo>=0.53',
|
||||||
'django-ckeditor<=4.5.3',
|
'django-ckeditor<=4.5.3',
|
||||||
|
'django-ratelimit<3',
|
||||||
'XStatic-Leaflet',
|
'XStatic-Leaflet',
|
||||||
'pyproj',
|
'pyproj',
|
||||||
],
|
],
|
||||||
|
|
|
@ -68,3 +68,12 @@ def sms_mocking():
|
||||||
def http_requests():
|
def http_requests():
|
||||||
with HttpRequestsMocking() as http_requests:
|
with HttpRequestsMocking() as http_requests:
|
||||||
yield 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)
|
t1, t2 = z1.read(name), z2.read(name)
|
||||||
assert t1 == t2, 'file "%s" differs' % name
|
assert t1 == t2, 'file "%s" differs' % name
|
||||||
|
|
||||||
|
|
||||||
def pytest_generate_tests(metafunc):
|
def pytest_generate_tests(metafunc):
|
||||||
if 'pub' in metafunc.fixturenames:
|
if 'pub' in metafunc.fixturenames:
|
||||||
metafunc.parametrize('pub', ['pickle', 'sql', 'pickle-templates', 'pickle-lazy'], indirect=True)
|
metafunc.parametrize('pub', ['pickle', 'sql', 'pickle-templates', 'pickle-lazy'], indirect=True)
|
||||||
|
@ -1311,7 +1310,7 @@ def get_displayed_tracking_code(resp):
|
||||||
break
|
break
|
||||||
return tracking_code
|
return tracking_code
|
||||||
|
|
||||||
def test_form_tracking_code(pub):
|
def test_form_tracking_code(pub, nocache):
|
||||||
formdef = create_formdef()
|
formdef = create_formdef()
|
||||||
formdef.fields = [fields.StringField(id='0', label='string')]
|
formdef.fields = [fields.StringField(id='0', label='string')]
|
||||||
formdef.enable_tracking_codes = True
|
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
|
assert resp.location == 'http://example.net/test/%s' % formdata_id
|
||||||
resp = resp.follow()
|
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)
|
user = create_user(pub)
|
||||||
formdef = create_formdef()
|
formdef = create_formdef()
|
||||||
formdef.fields = [fields.StringField(id='0', label='string')]
|
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,
|
resp = app.get('/code/%s/load' % tracking_code,
|
||||||
headers={'User-agent': 'Googlebot'}, status=403)
|
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 = create_formdef()
|
||||||
formdef.fields = [fields.StringField(id='0', label='string')]
|
formdef.fields = [fields.StringField(id='0', label='string')]
|
||||||
formdef.enable_tracking_codes = True
|
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
|
assert resp.location == 'http://example.net/code/%s/load' % tracking_code
|
||||||
resp = resp.follow(status=404)
|
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 = create_formdef()
|
||||||
formdef.data_class().wipe()
|
formdef.data_class().wipe()
|
||||||
formdef.fields = [fields.StringField(id='0', label='string'),
|
formdef.fields = [fields.StringField(id='0', label='string'),
|
||||||
|
@ -1541,7 +1548,7 @@ def test_form_tracking_code_email(pub, emails):
|
||||||
resp = resp.follow()
|
resp = resp.follow()
|
||||||
assert resp.forms[1]['f0'].value == 'barfoo'
|
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 = create_formdef()
|
||||||
formdef.fields = [fields.StringField(id='0', label='string')]
|
formdef.fields = [fields.StringField(id='0', label='string')]
|
||||||
formdef.enable_tracking_codes = True
|
formdef.enable_tracking_codes = True
|
||||||
|
@ -1587,7 +1594,7 @@ def test_form_tracking_code_remove_draft(pub):
|
||||||
assert resp.location == 'http://example.net/'
|
assert resp.location == 'http://example.net/'
|
||||||
assert formdef.data_class().count() == 0
|
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 = create_formdef()
|
||||||
formdef.fields = [fields.StringField(id='0', label='string')]
|
formdef.fields = [fields.StringField(id='0', label='string')]
|
||||||
formdef.enable_tracking_codes = True
|
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 resp.location == 'http://example.net/'
|
||||||
assert formdef.data_class().count() == 0
|
assert formdef.data_class().count() == 0
|
||||||
|
|
||||||
def test_form_discard_draft(pub):
|
def test_form_discard_draft(pub, nocache):
|
||||||
user = create_user(pub)
|
user = create_user(pub)
|
||||||
|
|
||||||
formdef = create_formdef()
|
formdef = create_formdef()
|
||||||
|
@ -1733,7 +1740,7 @@ def test_form_discard_draft(pub):
|
||||||
resp = resp.forms[1].submit('cancel')
|
resp = resp.forms[1].submit('cancel')
|
||||||
assert [x.status for x in formdef.data_class().select()] == ['draft']
|
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 = create_formdef()
|
||||||
formdef.fields = [fields.StringField(id='0', label='string')]
|
formdef.fields = [fields.StringField(id='0', label='string')]
|
||||||
formdef.enable_tracking_codes = True
|
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
|
assert resp.location == 'http://example.net/code/%s/load' % code.id
|
||||||
resp = resp.follow(status=404)
|
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 = create_formdef()
|
||||||
formdef.fields = [fields.PageField(id='0', label='1st page', type='page'),
|
formdef.fields = [fields.PageField(id='0', label='1st page', type='page'),
|
||||||
fields.StringField(id='1', label='string'),
|
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.six import StringIO
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
|
import ratelimit.utils
|
||||||
|
|
||||||
from quixote import (get_publisher, get_request, get_response, get_session,
|
from quixote import (get_publisher, get_request, get_response, get_session,
|
||||||
get_session_manager, redirect)
|
get_session_manager, redirect)
|
||||||
from quixote.directory import Directory, AccessControlled
|
from quixote.directory import Directory, AccessControlled
|
||||||
|
@ -148,6 +150,16 @@ class TrackingCodeDirectory(Directory):
|
||||||
return r.getvalue()
|
return r.getvalue()
|
||||||
|
|
||||||
def load(self):
|
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:
|
try:
|
||||||
tracking_code = get_publisher().tracking_code_class.get(self.code)
|
tracking_code = get_publisher().tracking_code_class.get(self.code)
|
||||||
if tracking_code.formdata_id is None:
|
if tracking_code.formdata_id is None:
|
||||||
|
|
Loading…
Reference in New Issue