Skip to content
Snippets Groups Projects
Commit 9f287165 authored by Gabe Mulley's avatar Gabe Mulley
Browse files

log registration failures

parent cb0e6f2a
No related branches found
No related tags found
No related merge requests found
......@@ -56,6 +56,7 @@ from openedx.core.djangoapps.user_authn.views.registration_form import (
AccountCreationForm,
RegistrationFormFactory
)
from openedx.core.djangoapps.waffle_utils import WaffleFlag, WaffleFlagNamespace
from student.helpers import (
authenticate_new_user,
create_or_set_user_attribute_created_on_site,
......@@ -94,6 +95,24 @@ REGISTRATION_UTM_CREATED_AT = 'registration_utm_created_at'
REGISTER_USER = Signal(providing_args=["user", "registration"])
# .. feature_toggle_name: registration.enable_failure_logging
# .. feature_toggle_type: flag
# .. feature_toggle_default: False
# .. feature_toggle_description: Enable verbose logging of registration failure messages
# .. feature_toggle_category: registration
# .. feature_toggle_use_cases: monitored_rollout
# .. feature_toggle_creation_date: 2020-04-30
# .. feature_toggle_expiration_date: 2020-06-01
# .. feature_toggle_warnings: None
# .. feature_toggle_tickets: None
# .. feature_toggle_status: supported
REGISTRATION_FAILURE_LOGGING_FLAG = WaffleFlag(
waffle_namespace=WaffleFlagNamespace(name=u'registration'),
flag_name=u'enable_failure_logging',
flag_undefined_default=False
)
@transaction.non_atomic_requests
def create_account_with_params(request, params):
"""
......@@ -463,7 +482,7 @@ class RegistrationView(APIView):
data = request.POST.copy()
self._handle_terms_of_service(data)
response = self._handle_duplicate_email_username(data)
response = self._handle_duplicate_email_username(request, data)
if response:
return response
......@@ -471,11 +490,11 @@ class RegistrationView(APIView):
if response:
return response
response = self._create_response({}, status_code=200)
response = self._create_response(request, {}, status_code=200)
set_logged_in_cookies(request, response, user)
return response
def _handle_duplicate_email_username(self, data):
def _handle_duplicate_email_username(self, request, data):
# pylint: disable=no-member
# TODO Verify whether this check is needed here - it may be duplicated in user_api.
email = data.get('email')
......@@ -489,7 +508,7 @@ class RegistrationView(APIView):
errors["username"] = [{"user_message": accounts_settings.USERNAME_CONFLICT_MSG.format(username=username)}]
if errors:
return self._create_response(errors, status_code=409)
return self._create_response(request, errors, status_code=409)
def _handle_terms_of_service(self, data):
# Backwards compatibility: the student view expects both
......@@ -510,7 +529,7 @@ class RegistrationView(APIView):
errors = {
err.field: [{"user_message": text_type(err)}]
}
response = self._create_response(errors, status_code=409)
response = self._create_response(request, errors, status_code=409)
except ValidationError as err:
# Should only get field errors from this exception
assert NON_FIELD_ERRORS not in err.message_dict
......@@ -519,18 +538,39 @@ class RegistrationView(APIView):
field: [{"user_message": error} for error in error_list]
for field, error_list in err.message_dict.items()
}
response = self._create_response(errors, status_code=400)
response = self._create_response(request, errors, status_code=400)
except PermissionDenied:
response = HttpResponseForbidden(_("Account creation not allowed."))
return response, user
def _create_response(self, response_dict, status_code):
def _create_response(self, request, response_dict, status_code):
if status_code == 200:
# keeping this `success` field in for now, as we have outstanding clients expecting this
response_dict['success'] = True
else:
self._log_validation_errors(request, response_dict, status_code)
return JsonResponse(response_dict, status=status_code)
def _log_validation_errors(self, request, errors, status_code):
if not REGISTRATION_FAILURE_LOGGING_FLAG.is_enabled():
return
try:
for field_key, errors in errors.items():
for error in errors:
log.info(
'message=registration_failed, status_code=%d, agent="%s", field="%s", error="%s"',
status_code,
request.META.get('HTTP_USER_AGENT', ''),
field_key,
error['user_message']
)
except: # pylint: disable=bare-except
log.exception("Failed to log registration validation error")
pass
class RegistrationValidationThrottle(AnonRateThrottle):
"""
......
......@@ -50,7 +50,9 @@ from openedx.core.djangoapps.user_api.accounts.tests.retirement_helpers import (
from openedx.core.djangoapps.user_api.tests.test_helpers import TestCaseForm
from openedx.core.djangoapps.user_api.tests.test_constants import SORTED_COUNTRIES
from openedx.core.djangoapps.user_api.tests.test_views import UserAPITestCase
from openedx.core.djangoapps.user_authn.views.register import RegistrationValidationThrottle
from openedx.core.djangoapps.user_authn.views.register import RegistrationValidationThrottle, \
REGISTRATION_FAILURE_LOGGING_FLAG
from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
from openedx.core.lib.api import test_utils
from student.helpers import authenticate_new_user
......@@ -209,6 +211,42 @@ class RegistrationViewValidationErrorTest(ThirdPartyAuthTestMixin, UserAPITestCa
}
)
@override_waffle_flag(REGISTRATION_FAILURE_LOGGING_FLAG, True)
def test_registration_failure_logging(self):
# Register a user
response = self.client.post(self.url, {
"email": self.EMAIL,
"name": self.NAME,
"username": self.USERNAME,
"password": self.PASSWORD,
"honor_code": "true",
})
self.assertHttpOK(response)
# Try to create a second user with the same email address
response = self.client.post(self.url, {
"email": self.EMAIL,
"name": "Someone Else",
"username": "someone_else",
"password": self.PASSWORD,
"honor_code": "true",
})
self.assertEqual(response.status_code, 409)
response_json = json.loads(response.content.decode('utf-8'))
self.assertDictEqual(
response_json,
{
"email": [{
"user_message": (
u"It looks like {} belongs to an existing account. "
"Try again with a different email address."
).format(
self.EMAIL
)
}]
}
)
def test_register_duplicate_username_account_validation_error(self):
# Register the first user
response = self.client.post(self.url, {
......
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