Projet

Général

Profil

0001-misc-add-rate-limiting-to-tracking-code-URLs-35395.patch

Frédéric Péters, 14 août 2019 09:43

Télécharger (6,29 ko)

Voir les différences:

Subject: [PATCH] misc: add rate limiting to tracking code URLs (#35395)

 combo/apps/wcs/views.py | 22 +++++++++++++++++++---
 combo/settings.py       |  3 +++
 debian/control          |  1 +
 setup.py                |  1 +
 tests/conftest.py       |  9 +++++++++
 tests/test_wcs.py       | 12 ++++++++++--
 6 files changed, 43 insertions(+), 5 deletions(-)
combo/apps/wcs/views.py
17 17
import re
18 18

  
19 19
from django.contrib import messages
20
from django.conf import settings
21
from django.core.exceptions import PermissionDenied
20 22
from django.core.urlresolvers import reverse
21 23
from django.http import JsonResponse, HttpResponseRedirect, HttpResponseBadRequest
22 24
from django.utils.http import urlquote
......
25 27
from django.views.decorators.csrf import csrf_exempt
26 28
from django.views.generic import View
27 29

  
30
import ratelimit.utils
31

  
28 32
from .models import TrackingCodeInputCell
29 33
from .utils import get_wcs_services
30 34

  
......
41 45
        return super(TrackingCodeView, self).dispatch(*args, **kwargs)
42 46

  
43 47
    @classmethod
44
    def search(self, code, wcs_site=None):
48
    def search(self, code, request, wcs_site=None):
45 49
        code = code.strip().upper()
46 50
        if wcs_site:
47 51
            wcs_sites = [get_wcs_services().get(wcs_site)]
48 52
        else:
49 53
            wcs_sites = get_wcs_services().values()
50 54

  
55
        rate_limit_option = settings.WCS_TRACKING_CODE_RATE_LIMIT
56
        if rate_limit_option and rate_limit_option != 'none':
57
            for rate_limit in rate_limit_option.split():
58
                ratelimited = ratelimit.utils.is_ratelimited(
59
                        request=request,
60
                        group='trackingcode',
61
                        key='ip',
62
                        rate=rate_limit,
63
                        increment=True)
64
                if ratelimited:
65
                    raise PermissionDenied('rate limit reached (%s)' % rate_limit)
66

  
51 67
        for wcs_site in wcs_sites:
52 68
            response = requests.get('/api/code/' + urlquote(code),
53 69
                    remote_service=wcs_site, log_errors=False)
......
65 81
            return HttpResponseBadRequest('Missing code')
66 82
        code = request.POST['code']
67 83

  
68
        url = self.search(code, wcs_site=cell.wcs_site)
84
        url = self.search(code, request, wcs_site=cell.wcs_site)
69 85
        if url:
70 86
            return HttpResponseRedirect(url)
71 87

  
......
89 105
    query = request.GET.get('q') or ''
90 106
    query = query.strip().upper()
91 107
    if re.match(r'^[BCDFGHJKLMNPQRSTVWXZ]{8}$', query):
92
        url = TrackingCodeView.search(query)
108
        url = TrackingCodeView.search(query, request)
93 109
        if url:
94 110
            hits.append({
95 111
                'text': _('Use tracking code %s') % query,
combo/settings.py
310 310
# default duration of notifications (in days)
311 311
COMBO_DEFAULT_NOTIFICATION_DURATION = 3
312 312

  
313
# tracking code thorttling
314
WCS_TRACKING_CODE_RATE_LIMIT = '3/s 1500/d'
315

  
313 316
# predefined slots for assets
314 317
# example: {'banner': {'label': 'Banner image'}}
315 318
COMBO_ASSET_SLOTS = {}
debian/control
22 22
    python-xstatic-roboto-fontface (<< 0.5.0.0),
23 23
    python-eopayment (>= 1.35),
24 24
    python-django-haystack (>= 2.4.0),
25
    python-django-ratelimit,
25 26
    python-sorl-thumbnail,
26 27
    python-pil,
27 28
    python-pywebpush,
setup.py
161 161
        'python-dateutil',
162 162
        'djangorestframework>=3.3, <3.7',
163 163
        'django-haystack',
164
        'django-ratelimit<3',
164 165
        'whoosh',
165 166
        'sorl-thumbnail',
166 167
        'Pillow',
tests/conftest.py
50 50
    except User.DoesNotExist:
51 51
        user = User.objects.create_superuser('admin', email=None, password='admin')
52 52
    return user
53

  
54

  
55
@pytest.fixture
56
def nocache(settings):
57
    settings.CACHES = {
58
        'default': {
59
            'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
60
        }
61
    }
tests/test_wcs.py
611 611

  
612 612

  
613 613
@wcs_present
614
def test_tracking_code_cell(app):
614
def test_tracking_code_cell(app, nocache):
615 615
    Page.objects.all().delete()
616 616
    page = Page(title='One', slug='index', template_name='standard')
617 617
    page.save()
......
713 713
        assert u'>Picture — form title (test)<' in resp.text
714 714

  
715 715
@wcs_present
716
def test_tracking_code_search(app):
716
def test_tracking_code_search(app, nocache):
717 717
    assert len(app.get('/api/search/tracking-code/').json.get('data')) == 0
718 718
    assert len(app.get('/api/search/tracking-code/?q=123').json.get('data')) == 0
719 719
    assert len(app.get('/api/search/tracking-code/?q=BBCCDFF').json.get('data')) == 0
......
722 722
    assert len(app.get('/api/search/tracking-code/?q=BBCCDDFFG').json.get('data')) == 0
723 723
    assert len(app.get('/api/search/tracking-code/?q= cnphntfb').json.get('data')) == 1
724 724

  
725
@wcs_present
726
def test_tracking_code_search_rate_limit(app):
727
    app.get('/api/search/tracking-code/?q=BBCCDDFF')
728
    app.get('/api/search/tracking-code/?q=BBCCDDFF')
729
    app.get('/api/search/tracking-code/?q=BBCCDDFF')
730
    app.get('/api/search/tracking-code/?q=BBCCDDFF', status=403)
731
    app.get('/api/search/tracking-code/?q=BBCCDDFF', status=403)
732

  
725 733
@wcs_present
726 734
def test_wcs_search_engines(app):
727 735
    search_engines = engines.get_engines()
728
-