From 8761eaf3dd020ae2b5b5371cf9f44fb158bd56b4 Mon Sep 17 00:00:00 2001 From: Will Daly Date: Thu, 16 Oct 2014 10:20:21 -0400 Subject: [PATCH] Added check for duplicate email and username --- common/djangoapps/user_api/api/account.py | 30 +++++++++ .../djangoapps/user_api/tests/test_views.py | 63 ++++++++++++++++--- common/djangoapps/user_api/views.py | 14 +++++ 3 files changed, 97 insertions(+), 10 deletions(-) diff --git a/common/djangoapps/user_api/api/account.py b/common/djangoapps/user_api/api/account.py index 4281326860..0bc66fb267 100644 --- a/common/djangoapps/user_api/api/account.py +++ b/common/djangoapps/user_api/api/account.py @@ -1,11 +1,13 @@ """Python API for user accounts. + Account information includes a student's username, password, and email address, but does NOT include user profile information (i.e., demographic information and preferences). """ from django.db import transaction, IntegrityError +from django.db.models import Q from django.core.validators import validate_email, validate_slug, ValidationError from user_api.models import User, UserProfile, Registration, PendingEmailChange from user_api.helpers import intercept_errors @@ -138,6 +140,34 @@ def create_account(username, password, email): return registration.activation_key +def check_account_exists(username=None, email=None): + """Check whether an account with a particular username or email already exists. + + Keyword Arguments: + username (unicode) + email (unicode) + + Returns: + list of conflicting fields + + Example Usage: + >>> account_api.check_account_exists(username="bob") + [] + >>> account_api.check_account_exists(username="ted", email="ted@example.com") + ["email", "username"] + + """ + conflicts = [] + + if email is not None and User.objects.filter(email=email).exists(): + conflicts.append("email") + + if username is not None and User.objects.filter(username=username).exists(): + conflicts.append("username") + + return conflicts + + @intercept_errors(AccountInternalError, ignore_errors=[AccountRequestError]) def account_info(username): """Retrieve information about a user's account. diff --git a/common/djangoapps/user_api/tests/test_views.py b/common/djangoapps/user_api/tests/test_views.py index a8f25b5085..5677c0b99f 100644 --- a/common/djangoapps/user_api/tests/test_views.py +++ b/common/djangoapps/user_api/tests/test_views.py @@ -1037,7 +1037,6 @@ class RegistrationViewTest(ApiTestCase): response = self.client.post(self.url, data) self.assertHttpBadRequest(response) - @override_settings(REGISTRATION_EXTRA_FIELDS={"country": "required"}) @ddt.data("email", "name", "username", "password", "country") def test_register_missing_required_field(self, missing_field): @@ -1055,21 +1054,65 @@ class RegistrationViewTest(ApiTestCase): response = self.client.post(self.url, data) self.assertHttpBadRequest(response) - def test_register_already_authenticated(self): - data = { + def test_register_duplicate_email(self): + # Register the first user + response = self.client.post(self.url, { "email": self.EMAIL, "name": self.NAME, "username": self.USERNAME, "password": self.PASSWORD, - } - - # Register once, which will also log us in - response = self.client.post(self.url, data) + }) self.assertHttpOK(response) - # Try to register again - response = self.client.post(self.url, data) - self.assertHttpBadRequest(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, + }) + self.assertEqual(response.status_code, 409) + self.assertEqual(response.content, json.dumps(["email"])) + + def test_register_duplicate_username(self): + # Register the first user + response = self.client.post(self.url, { + "email": self.EMAIL, + "name": self.NAME, + "username": self.USERNAME, + "password": self.PASSWORD, + }) + self.assertHttpOK(response) + + # Try to create a second user with the same username + response = self.client.post(self.url, { + "email": "someone+else@example.com", + "name": "Someone Else", + "username": self.USERNAME, + "password": self.PASSWORD, + }) + self.assertEqual(response.status_code, 409) + self.assertEqual(response.content, json.dumps(["username"])) + + def test_register_duplicate_username_and_email(self): + # Register the first user + response = self.client.post(self.url, { + "email": self.EMAIL, + "name": self.NAME, + "username": self.USERNAME, + "password": self.PASSWORD, + }) + self.assertHttpOK(response) + + # Try to create a second user with the same username + response = self.client.post(self.url, { + "email": self.EMAIL, + "name": "Someone Else", + "username": self.USERNAME, + "password": self.PASSWORD, + }) + self.assertEqual(response.status_code, 409) + self.assertEqual(response.content, json.dumps(["email", "username"])) def _assert_reg_field(self, extra_fields_setting, expected_field): """Retrieve the registration form description from the server and diff --git a/common/djangoapps/user_api/views.py b/common/djangoapps/user_api/views.py index 5d7254b330..b25814a748 100644 --- a/common/djangoapps/user_api/views.py +++ b/common/djangoapps/user_api/views.py @@ -1,4 +1,5 @@ """HTTP end-points for the User API. """ +import json from django.conf import settings from django.contrib.auth.models import User @@ -177,6 +178,7 @@ class RegistrationView(APIView): return HttpResponse(form_desc.to_json(), content_type="application/json") @method_decorator(ensure_csrf_cookie) + @method_decorator(require_post_params(DEFAULT_FIELDS)) def post(self, request): """Create the user's account. @@ -198,6 +200,18 @@ class RegistrationView(APIView): request.POST["honor_code"] = "true" request.POST["terms_of_service"] = "true" + # Handle duplicate username/email + conflicts = account_api.check_account_exists( + username=request.POST.get('username'), + email=request.POST.get('email') + ) + if conflicts: + return HttpResponse( + status=409, + content=json.dumps(conflicts), + content_type="application/json" + ) + # For the initial implementation, shim the existing login view # from the student Django app. from student.views import create_account