Commit 8761eaf3 by Will Daly

Added check for duplicate email and username

parent 7ef9ec83
"""Python API for user accounts. """Python API for user accounts.
Account information includes a student's username, password, and email Account information includes a student's username, password, and email
address, but does NOT include user profile information (i.e., demographic address, but does NOT include user profile information (i.e., demographic
information and preferences). information and preferences).
""" """
from django.db import transaction, IntegrityError from django.db import transaction, IntegrityError
from django.db.models import Q
from django.core.validators import validate_email, validate_slug, ValidationError from django.core.validators import validate_email, validate_slug, ValidationError
from user_api.models import User, UserProfile, Registration, PendingEmailChange from user_api.models import User, UserProfile, Registration, PendingEmailChange
from user_api.helpers import intercept_errors from user_api.helpers import intercept_errors
...@@ -138,6 +140,34 @@ def create_account(username, password, email): ...@@ -138,6 +140,34 @@ def create_account(username, password, email):
return registration.activation_key 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]) @intercept_errors(AccountInternalError, ignore_errors=[AccountRequestError])
def account_info(username): def account_info(username):
"""Retrieve information about a user's account. """Retrieve information about a user's account.
......
...@@ -1037,7 +1037,6 @@ class RegistrationViewTest(ApiTestCase): ...@@ -1037,7 +1037,6 @@ class RegistrationViewTest(ApiTestCase):
response = self.client.post(self.url, data) response = self.client.post(self.url, data)
self.assertHttpBadRequest(response) self.assertHttpBadRequest(response)
@override_settings(REGISTRATION_EXTRA_FIELDS={"country": "required"}) @override_settings(REGISTRATION_EXTRA_FIELDS={"country": "required"})
@ddt.data("email", "name", "username", "password", "country") @ddt.data("email", "name", "username", "password", "country")
def test_register_missing_required_field(self, missing_field): def test_register_missing_required_field(self, missing_field):
...@@ -1055,21 +1054,65 @@ class RegistrationViewTest(ApiTestCase): ...@@ -1055,21 +1054,65 @@ class RegistrationViewTest(ApiTestCase):
response = self.client.post(self.url, data) response = self.client.post(self.url, data)
self.assertHttpBadRequest(response) self.assertHttpBadRequest(response)
def test_register_already_authenticated(self): def test_register_duplicate_email(self):
data = { # Register the first user
response = self.client.post(self.url, {
"email": self.EMAIL, "email": self.EMAIL,
"name": self.NAME, "name": self.NAME,
"username": self.USERNAME, "username": self.USERNAME,
"password": self.PASSWORD, "password": self.PASSWORD,
} })
self.assertHttpOK(response)
# Register once, which will also log us in # Try to create a second user with the same email address
response = self.client.post(self.url, data) 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) self.assertHttpOK(response)
# Try to register again # Try to create a second user with the same username
response = self.client.post(self.url, data) response = self.client.post(self.url, {
self.assertHttpBadRequest(response) "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): def _assert_reg_field(self, extra_fields_setting, expected_field):
"""Retrieve the registration form description from the server and """Retrieve the registration form description from the server and
......
"""HTTP end-points for the User API. """ """HTTP end-points for the User API. """
import json
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
...@@ -177,6 +178,7 @@ class RegistrationView(APIView): ...@@ -177,6 +178,7 @@ class RegistrationView(APIView):
return HttpResponse(form_desc.to_json(), content_type="application/json") return HttpResponse(form_desc.to_json(), content_type="application/json")
@method_decorator(ensure_csrf_cookie) @method_decorator(ensure_csrf_cookie)
@method_decorator(require_post_params(DEFAULT_FIELDS))
def post(self, request): def post(self, request):
"""Create the user's account. """Create the user's account.
...@@ -198,6 +200,18 @@ class RegistrationView(APIView): ...@@ -198,6 +200,18 @@ class RegistrationView(APIView):
request.POST["honor_code"] = "true" request.POST["honor_code"] = "true"
request.POST["terms_of_service"] = "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 # For the initial implementation, shim the existing login view
# from the student Django app. # from the student Django app.
from student.views import create_account from student.views import create_account
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment