Add rate limit to registration endpoint (#27060)

Currently the registration endpoint has no rate limit. Added a new ratelimit
variable to support the change, it's value is set to 60/7d.

VAN-302
This commit is contained in:
Zainab Amir
2021-03-25 16:28:30 +05:00
committed by GitHub
parent 671ad883fc
commit 8cc5f13daf
10 changed files with 50 additions and 0 deletions

View File

@@ -2380,6 +2380,7 @@ RESET_PASSWORD_API_RATELIMIT = '30/7d'
##### REGISTRATION RATE LIMIT SETTINGS #####
REGISTRATION_VALIDATION_RATELIMIT = '30/7d'
REGISTRATION_RATELIMIT = '60/7d'
##### PASSWORD RESET RATE LIMIT SETTINGS #####
PASSWORD_RESET_IP_RATE = '1/m'

View File

@@ -266,6 +266,7 @@ TIME_ZONE = ENV_TOKENS.get('CELERY_TIMEZONE', CELERY_TIMEZONE)
REGISTRATION_VALIDATION_RATELIMIT = ENV_TOKENS.get(
'REGISTRATION_VALIDATION_RATELIMIT', REGISTRATION_VALIDATION_RATELIMIT
)
REGISTRATION_RATELIMIT = ENV_TOKENS.get('REGISTRATION_RATELIMIT', REGISTRATION_RATELIMIT)
# Push to LMS overrides
GIT_REPO_EXPORT_DIR = ENV_TOKENS.get('GIT_REPO_EXPORT_DIR', '/edx/var/edxapp/export_course_repos')

View File

@@ -330,6 +330,8 @@ LOGISTRATION_PER_EMAIL_RATELIMIT_RATE = '6/5m'
LOGISTRATION_API_RATELIMIT = '5/m'
REGISTRATION_VALIDATION_RATELIMIT = '5/minute'
REGISTRATION_RATELIMIT = '5/minute'
RESET_PASSWORD_TOKEN_VALIDATE_API_RATELIMIT = '2/m'
RESET_PASSWORD_API_RATELIMIT = '2/m'

View File

@@ -2,11 +2,13 @@
import json
from django.test import TestCase
from django.test.utils import override_settings
from django.urls import reverse
from openedx.core.djangoapps.user_api.accounts import USERNAME_BAD_LENGTH_MSG
@override_settings(RATELIMIT_ENABLE=False)
class TestLongUsernameEmail(TestCase): # lint-amnesty, pylint: disable=missing-class-docstring
def setUp(self):

View File

@@ -11,6 +11,7 @@ from django.urls import reverse
from common.djangoapps.util.password_policy_validators import create_validator_config
@override_settings(RATELIMIT_ENABLE=False)
class TestPasswordPolicy(TestCase):
"""
Go through some password policy tests to make sure things are properly working
@@ -226,6 +227,7 @@ class TestPasswordPolicy(TestCase):
assert obj['success']
@override_settings(RATELIMIT_ENABLE=False)
class TestUsernamePasswordNonmatch(TestCase):
"""
Test that registration username and password fields differ

View File

@@ -3160,6 +3160,7 @@ REST_FRAMEWORK = {
}
REGISTRATION_VALIDATION_RATELIMIT = '30/7d'
REGISTRATION_RATELIMIT = '60/7d'
SWAGGER_SETTINGS = {
'DEFAULT_INFO': 'openedx.core.apidocs.api_info',

View File

@@ -613,6 +613,8 @@ REGISTRATION_VALIDATION_RATELIMIT = ENV_TOKENS.get(
'REGISTRATION_VALIDATION_RATELIMIT', REGISTRATION_VALIDATION_RATELIMIT
)
REGISTRATION_RATELIMIT = ENV_TOKENS.get('REGISTRATION_RATELIMIT', REGISTRATION_RATELIMIT)
#### PASSWORD POLICY SETTINGS #####
AUTH_PASSWORD_VALIDATORS = ENV_TOKENS.get("AUTH_PASSWORD_VALIDATORS", AUTH_PASSWORD_VALIDATORS)

View File

@@ -597,5 +597,7 @@ LOGISTRATION_PER_EMAIL_RATELIMIT_RATE = '6/5m'
LOGISTRATION_API_RATELIMIT = '5/m'
REGISTRATION_VALIDATION_RATELIMIT = '5/minute'
REGISTRATION_RATELIMIT = '5/minute'
RESET_PASSWORD_TOKEN_VALIDATE_API_RATELIMIT = '2/m'
RESET_PASSWORD_API_RATELIMIT = '2/m'

View File

@@ -492,6 +492,7 @@ class RegistrationView(APIView):
content_type="application/json")
@method_decorator(csrf_exempt)
@method_decorator(ratelimit(key=REAL_IP_KEY, rate=settings.REGISTRATION_RATELIMIT, method='POST'))
def post(self, request):
"""Create the user's account.
@@ -510,6 +511,10 @@ class RegistrationView(APIView):
address already exists
HttpResponse: 403 operation not allowed
"""
should_be_rate_limited = getattr(request, 'limited', False)
if should_be_rate_limited:
return JsonResponse({'error_code': 'forbidden-request'}, status=403)
if is_require_third_party_auth_enabled() and not pipeline.running(request):
# if request is not running a third-party auth pipeline
return HttpResponseForbidden(

View File

@@ -1632,6 +1632,38 @@ class RegistrationViewTestV1(ThirdPartyAuthTestMixin, UserAPITestCase):
response = self.client.post(self.url, {"email": self.EMAIL, "username": self.USERNAME})
assert response.status_code == 403
@override_settings(
CACHES={
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'registration_proxy',
}
}
)
def test_rate_limiting_registration_view(self):
"""
Confirm rate limits work as expected for registration
end point.
Note that drf's rate limiting makes use of the default cache
to enforce limits; that's why this test needs a "real"
default cache (as opposed to the usual-for-tests DummyCache)
"""
payload = {
"email": 'email',
"name": self.NAME,
"username": self.USERNAME,
"password": self.PASSWORD,
"honor_code": "true",
}
for _ in range(int(settings.REGISTRATION_RATELIMIT.split('/')[0])):
response = self.client.post(self.url, payload)
assert response.status_code != 403
response = self.client.post(self.url, payload)
assert response.status_code == 403
cache.clear()
def _assert_fields_match(self, actual_field, expected_field):
"""
Assert that the actual field and the expected field values match.