Skip to content
Snippets Groups Projects
Commit 74bc970e authored by Waheed Ahmed's avatar Waheed Ahmed
Browse files

Rate limit logistration endpoints.

PROD-1506
parent 2d2015f4
No related branches found
No related tags found
No related merge requests found
...@@ -7,6 +7,7 @@ import logging ...@@ -7,6 +7,7 @@ import logging
import six import six
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.http import HttpResponseForbidden
from django.shortcuts import redirect from django.shortcuts import redirect
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
...@@ -34,6 +35,7 @@ from student.helpers import get_next_url_for_login_page ...@@ -34,6 +35,7 @@ from student.helpers import get_next_url_for_login_page
from third_party_auth import pipeline from third_party_auth import pipeline
from third_party_auth.decorators import xframe_allow_whitelisted from third_party_auth.decorators import xframe_allow_whitelisted
from util.password_policy_validators import DEFAULT_MAX_PASSWORD_LENGTH from util.password_policy_validators import DEFAULT_MAX_PASSWORD_LENGTH
from util.request_rate_limiter import BadRequestRateLimiter
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -135,6 +137,12 @@ def login_and_registration_form(request, initial_mode="login"): ...@@ -135,6 +137,12 @@ def login_and_registration_form(request, initial_mode="login"):
initial_mode (string): Either "login" or "register". initial_mode (string): Either "login" or "register".
""" """
limiter = BadRequestRateLimiter()
if limiter.is_rate_limit_exceeded(request):
log.warning("Rate limit exceeded in login and registration with initial mode [%s]", initial_mode)
return HttpResponseForbidden("Rate limit exceeded")
# Determine the URL to redirect to following login/registration/third_party_auth # Determine the URL to redirect to following login/registration/third_party_auth
redirect_to = get_next_url_for_login_page(request) redirect_to = get_next_url_for_login_page(request)
...@@ -230,6 +238,8 @@ def login_and_registration_form(request, initial_mode="login"): ...@@ -230,6 +238,8 @@ def login_and_registration_form(request, initial_mode="login"):
response = render_to_response('student_account/login_and_register.html', context) response = render_to_response('student_account/login_and_register.html', context)
handle_enterprise_cookies_for_logistration(request, response, context) handle_enterprise_cookies_for_logistration(request, response, context)
limiter.tick_request_counter(request)
return response return response
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" Tests for Logistration views. """ """ Tests for Logistration views. """
from datetime import datetime, timedelta
from http.cookies import SimpleCookie from http.cookies import SimpleCookie
import ddt import ddt
...@@ -17,6 +17,8 @@ from django.test.client import RequestFactory ...@@ -17,6 +17,8 @@ from django.test.client import RequestFactory
from django.test.utils import override_settings from django.test.utils import override_settings
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from freezegun import freeze_time
from pytz import UTC
from six.moves.urllib.parse import urlencode # pylint: disable=import-error from six.moves.urllib.parse import urlencode # pylint: disable=import-error
from course_modes.models import CourseMode from course_modes.models import CourseMode
...@@ -71,6 +73,25 @@ class LoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMixin, ModuleSto ...@@ -71,6 +73,25 @@ class LoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMixin, ModuleSto
expected_data = u'"initial_mode": "{mode}"'.format(mode=initial_mode) expected_data = u'"initial_mode": "{mode}"'.format(mode=initial_mode)
self.assertContains(response, expected_data) self.assertContains(response, expected_data)
def test_login_and_registration_form_ratelimited(self):
"""
Test that login enpoint allow only 30 requests for every 5 minutes.
"""
login_url = reverse('signin_user')
for i in range(30):
response = self.client.get(login_url)
self.assertEqual(response.status_code, 200)
# then the rate limiter should kick in and give a HttpForbidden response
response = self.client.get(login_url)
self.assertEqual(response.status_code, 403)
# now reset the time to 6 mins from now in future in order to unblock
reset_time = datetime.now(UTC) + timedelta(seconds=361)
with freeze_time(reset_time):
response = self.client.get(login_url)
self.assertEqual(response.status_code, 200)
@ddt.data("signin_user", "register_user") @ddt.data("signin_user", "register_user")
def test_login_and_registration_form_already_authenticated(self, url_name): def test_login_and_registration_form_already_authenticated(self, url_name):
# call the account registration api that sets the login cookies # call the account registration api that sets the login cookies
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment