Browse files

fixes bug 976731 - Using a valid API token should skip rate limiter, …

…r=AdrianGaudebert
  • Loading branch information...
1 parent eecb1a1 commit c1025be60dbadeeff7bc9eeecb20956817726cbb @peterbe peterbe committed Feb 25, 2014
View
57 webapp-django/crashstats/api/tests/test_views.py
@@ -1634,3 +1634,60 @@ def mocked_get(url, **options):
response = self.client.get(url, params, HTTP_AUTH_TOKEN=token.key)
eq_(response.status_code, 200)
+
+ @mock.patch('requests.get')
+ def test_hit_or_not_hit_ratelimit(self, rget):
+
+ def mocked_get(url, **options):
+ if '/crontabber_state/' in url:
+ return Response("""
+ {
+ "state": {
+ "automatic-emails": {
+ "next_run": "2013-04-01T22:20:01+00:00",
+ "first_run": "2013-03-15T16:25:01+00:00",
+ "depends_on": [],
+ "last_run": "2013-04-01T21:20:01+00:00",
+ "last_success": "2013-04-01T20:25:01+00:00",
+ "error_count": 0,
+ "last_error": {}
+ },
+ "ftpscraper": {
+ "next_run": "2013-04-01T22:20:09+00:00",
+ "first_run": "2013-03-07T07:05:51+00:00",
+ "depends_on": [],
+ "last_run": "2013-04-01T21:20:09+00:00",
+ "last_success": "2013-04-01T21:05:51+00:00",
+ "error_count": 0,
+ "last_error": {}
+ }
+ }
+ }
+ """)
+
+ raise NotImplementedError(url)
+
+ rget.side_effect = mocked_get
+
+ # doesn't matter much which model we use
+ url = reverse('api:model_wrapper', args=('CrontabberState',))
+
+ response = self.client.get(url)
+ eq_(response.status_code, 200)
+ # the rate limit is currently 10/min so it's easy to hit the limit
+ for __ in range(10):
+ response = self.client.get(url)
+ eq_(response.status_code, 429)
+
+ user = User.objects.create(username='test')
+ token = Token.objects.create(
+ user=user,
+ notes="Just for avoiding rate limit"
+ )
+
+ response = self.client.get(url, HTTP_AUTH_TOKEN=token.key)
+ eq_(response.status_code, 200)
+
+ for __ in range(10):
+ response = self.client.get(url)
+ eq_(response.status_code, 200)
View
10 webapp-django/crashstats/api/views.py
@@ -141,8 +141,16 @@ class FormWrapper(forms.Form):
)
+def _skip_ratelimit(request):
+ return request.user.is_authenticated()
+
+
@waffle_switch('app_api_all')
-@ratelimit(method=['GET', 'POST', 'PUT'], rate='1000/m')
+@ratelimit(
+ method=['GET', 'POST', 'PUT'],
+ rate='10/m',
+ skip_if=_skip_ratelimit
+)
@utils.add_CORS_header # must be before `utils.json_view`
@utils.json_view
def model_wrapper(request, model_name):
View
3 webapp-django/crashstats/tokens/forms.py
@@ -15,8 +15,9 @@ def __init__(self, *args, **kwargs):
self.fields['permissions'].choices = [
(x.pk, x.name) for x in possible_permissions
]
+ self.fields['permissions'].required = False
self.fields['permissions'].help_text = (
- 'Required. '
+ 'Optional. '
'These are the permissions you have been granted. '
'You can select one or more.'
)
View
9 webapp-django/crashstats/tokens/templates/tokens/home.html
@@ -30,8 +30,9 @@
<div class="panel">
<div class="body notitle">
<p>
- You need API Tokens to be able to connect to the API so that the API
- knows who you are and thus what permissions you have.
+ You need <b>API Tokens to be able to connect to the API</b> so that the API
+ knows who you are and thus what permissions you have.<br>
+ Using any <b>valid API Token</b> with your API calls means a <b>much higher rate limit</b>.
</p>
</div>
</div>
@@ -60,6 +61,8 @@
<ul>
{% for permission in token.permissions.all() %}
<li><code>{{ permission.name }}</code></li>
+ {% else %}
+ <li><em>None</em></li>
{% endfor %}
</ul>
</td>
@@ -118,7 +121,7 @@
</table>
</form>
{% else %}
- <p>You currentl do not have any permissions to generate tokens for.</p>
+ <p>You currently do not have any permissions to generate tokens for.</p>
{% endif %}
</div>
</div>
View
8 webapp-django/crashstats/tokens/views.py
@@ -49,6 +49,14 @@ def home(request):
possible_permissions=possible_permissions
)
else:
+ # This is surprisingly important!
+ # If you *have* permissions, you can actually create a
+ # token without selecting *any* permissions. The point of
+ # that is to avoid the rate limiter.
+ # If you don't have any permissions attached to your user
+ # account means you haven't been hand curated by any
+ # administrator and if that's the case you shouldn't be able
+ # avoid the rate limiter.
form = None
context['form'] = form

0 comments on commit c1025be

Please sign in to comment.