Commit 291004de by Greg Price

Factor create_account param validation into a form

parent ad86ef3b
...@@ -2,16 +2,23 @@ ...@@ -2,16 +2,23 @@
Utility functions for validating forms Utility functions for validating forms
""" """
from django import forms from django import forms
from django.core.exceptions import ValidationError
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.forms import PasswordResetForm from django.contrib.auth.forms import PasswordResetForm
from django.contrib.auth.hashers import UNUSABLE_PASSWORD from django.contrib.auth.hashers import UNUSABLE_PASSWORD
from django.contrib.auth.tokens import default_token_generator from django.contrib.auth.tokens import default_token_generator
from django.utils.http import int_to_base36 from django.utils.http import int_to_base36
from django.utils.translation import ugettext_lazy as _
from django.template import loader from django.template import loader
from django.conf import settings from django.conf import settings
from microsite_configuration import microsite from microsite_configuration import microsite
from util.password_policy_validators import (
validate_password_length,
validate_password_complexity,
validate_password_dictionary,
)
class PasswordResetFormNoActive(PasswordResetForm): class PasswordResetFormNoActive(PasswordResetForm):
...@@ -70,3 +77,160 @@ class PasswordResetFormNoActive(PasswordResetForm): ...@@ -70,3 +77,160 @@ class PasswordResetFormNoActive(PasswordResetForm):
subject = subject.replace('\n', '') subject = subject.replace('\n', '')
email = loader.render_to_string(email_template_name, context) email = loader.render_to_string(email_template_name, context)
send_mail(subject, email, from_email, [user.email]) send_mail(subject, email, from_email, [user.email])
class TrueField(forms.BooleanField):
"""
A boolean field that only accepts "true" (case-insensitive) as true
"""
def to_python(self, value):
# CheckboxInput converts string to bool by case-insensitive match to "true" or "false"
if value is True:
return value
else:
return None
_USERNAME_TOO_SHORT_MSG = _("Username must be minimum of two characters long")
_EMAIL_INVALID_MSG = _("A properly formatted e-mail is required")
_PASSWORD_INVALID_MSG = _("A valid password is required")
_NAME_TOO_SHORT_MSG = _("Your legal name must be a minimum of two characters long")
class AccountCreationForm(forms.Form):
"""
A form to for account creation data. It is currently only used for
validation, not rendering.
"""
# TODO: Resolve repetition
username = forms.SlugField(
min_length=2,
max_length=30,
error_messages={
"required": _USERNAME_TOO_SHORT_MSG,
"invalid": _("Username should only consist of A-Z and 0-9, with no spaces."),
"min_length": _USERNAME_TOO_SHORT_MSG,
"max_length": _("Username cannot be more than %(limit_value)s characters long"),
}
)
email = forms.EmailField(
max_length=75, # Limit per RFCs is 254, but User's email field in django 1.4 only takes 75
error_messages={
"required": _EMAIL_INVALID_MSG,
"invalid": _EMAIL_INVALID_MSG,
"max_length": _("Email cannot be more than %(limit_value)s characters long"),
}
)
password = forms.CharField(
min_length=2,
error_messages={
"required": _PASSWORD_INVALID_MSG,
"min_length": _PASSWORD_INVALID_MSG,
}
)
name = forms.CharField(
min_length=2,
error_messages={
"required": _NAME_TOO_SHORT_MSG,
"min_length": _NAME_TOO_SHORT_MSG,
}
)
def __init__(
self,
data=None,
extra_fields=None,
extended_profile_fields=None,
enforce_username_neq_password=False,
enforce_password_policy=False,
tos_required=True
):
super(AccountCreationForm, self).__init__(data)
extra_fields = extra_fields or {}
self.extended_profile_fields = extended_profile_fields or {}
self.enforce_username_neq_password = enforce_username_neq_password
self.enforce_password_policy = enforce_password_policy
if tos_required:
self.fields["terms_of_service"] = TrueField(
error_messages={"required": _("You must accept the terms of service.")}
)
# TODO: These messages don't say anything about minimum length
error_message_dict = {
"level_of_education": _("A level of education is required"),
"gender": _("Your gender is required"),
"year_of_birth": _("Your year of birth is required"),
"mailing_address": _("Your mailing address is required"),
"goals": _("A description of your goals is required"),
"city": _("A city is required"),
"country": _("A country is required")
}
for field_name, field_value in extra_fields.items():
if field_name not in self.fields:
if field_name == "honor_code":
if field_value == "required":
self.fields[field_name] = TrueField(
error_messages={
"required": _("To enroll, you must follow the honor code.")
}
)
else:
required = field_value == "required"
min_length = 1 if field_name in ("gender", "level_of_education") else 2
error_message = error_message_dict.get(
field_name,
_("You are missing one or more required fields")
)
self.fields[field_name] = forms.CharField(
required=required,
min_length=min_length,
error_messages={
"required": error_message,
"min_length": error_message,
}
)
for field in self.extended_profile_fields:
if field not in self.fields:
self.fields[field] = forms.CharField(required=False)
def clean_password(self):
"""Enforce password policies (if applicable)"""
password = self.cleaned_data["password"]
if (
self.enforce_username_neq_password and
"username" in self.cleaned_data and
self.cleaned_data["username"] == password
):
raise ValidationError(_("Username and password fields cannot match"))
if self.enforce_password_policy:
try:
validate_password_length(password)
validate_password_complexity(password)
validate_password_dictionary(password)
except ValidationError, err:
raise ValidationError(_("Password: ") + "; ".join(err.messages))
return password
def clean_year_of_birth(self):
"""
Parse year_of_birth to an integer, but just use None instead of raising
an error if it is malformed
"""
try:
year_str = self.cleaned_data["year_of_birth"]
return int(year_str) if year_str is not None else None
except ValueError:
return None
@property
def cleaned_extended_profile(self):
"""
Return a dictionary containing the extended_profile_fields and values
"""
return {
key: value
for key, value in self.cleaned_data.items()
if key in self.extended_profile_fields and value is not None
}
...@@ -8,26 +8,30 @@ from student.models import CourseEnrollment ...@@ -8,26 +8,30 @@ from student.models import CourseEnrollment
from opaque_keys import InvalidKeyError from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locations import SlashSeparatedCourseKey from opaque_keys.edx.locations import SlashSeparatedCourseKey
from student.forms import AccountCreationForm
from student.views import _do_create_account from student.views import _do_create_account
def get_random_post_override(): def make_random_form():
""" """
Generate unique user data for dummy users. Generate unique user data for dummy users.
""" """
identification = uuid.uuid4().hex[:8] identification = uuid.uuid4().hex[:8]
return { return AccountCreationForm(
data={
'username': 'user_{id}'.format(id=identification), 'username': 'user_{id}'.format(id=identification),
'email': 'email_{id}@example.com'.format(id=identification), 'email': 'email_{id}@example.com'.format(id=identification),
'password': '12345', 'password': '12345',
'name': 'User {id}'.format(id=identification), 'name': 'User {id}'.format(id=identification),
} },
tos_required=False
)
def create(num, course_key): def create(num, course_key):
"""Create num users, enrolling them in course_key if it's not None""" """Create num users, enrolling them in course_key if it's not None"""
for idx in range(num): for idx in range(num):
(user, user_profile, __) = _do_create_account(get_random_post_override()) (user, _, _) = _do_create_account(make_random_form())
if course_key is not None: if course_key is not None:
CourseEnrollment.enroll(user, course_key) CourseEnrollment.enroll(user, course_key)
......
...@@ -8,6 +8,7 @@ from django.utils import translation ...@@ -8,6 +8,7 @@ from django.utils import translation
from opaque_keys import InvalidKeyError from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locations import SlashSeparatedCourseKey from opaque_keys.edx.locations import SlashSeparatedCourseKey
from student.forms import AccountCreationForm
from student.models import CourseEnrollment, Registration, create_comments_service_user from student.models import CourseEnrollment, Registration, create_comments_service_user
from student.views import _do_create_account, AccountValidationError from student.views import _do_create_account, AccountValidationError
from track.management.tracked_command import TrackedCommand from track.management.tracked_command import TrackedCommand
...@@ -80,21 +81,22 @@ class Command(TrackedCommand): ...@@ -80,21 +81,22 @@ class Command(TrackedCommand):
except InvalidKeyError: except InvalidKeyError:
course = SlashSeparatedCourseKey.from_deprecated_string(options['course']) course = SlashSeparatedCourseKey.from_deprecated_string(options['course'])
post_data = { form = AccountCreationForm(
data={
'username': username, 'username': username,
'email': options['email'], 'email': options['email'],
'password': options['password'], 'password': options['password'],
'name': name, 'name': name,
'honor_code': u'true', },
'terms_of_service': u'true', tos_required=False
} )
# django.utils.translation.get_language() will be used to set the new # django.utils.translation.get_language() will be used to set the new
# user's preferred language. This line ensures that the result will # user's preferred language. This line ensures that the result will
# match this installation's default locale. Otherwise, inside a # match this installation's default locale. Otherwise, inside a
# management command, it will always return "en-us". # management command, it will always return "en-us".
translation.activate(settings.LANGUAGE_CODE) translation.activate(settings.LANGUAGE_CODE)
try: try:
user, profile, reg = _do_create_account(post_data) user, _, reg = _do_create_account(form)
if options['staff']: if options['staff']:
user.is_staff = True user.is_staff = True
user.save() user.save()
......
...@@ -93,13 +93,19 @@ class TestCreateAccount(TestCase): ...@@ -93,13 +93,19 @@ class TestCreateAccount(TestCase):
def test_profile_saved_no_optional_fields(self): def test_profile_saved_no_optional_fields(self):
profile = self.create_account_and_fetch_profile() profile = self.create_account_and_fetch_profile()
self.assertEqual(profile.name, self.params["name"]) self.assertEqual(profile.name, self.params["name"])
self.assertIsNone(profile.level_of_education) self.assertEqual(profile.level_of_education, "")
self.assertIsNone(profile.gender) self.assertEqual(profile.gender, "")
self.assertIsNone(profile.mailing_address) self.assertEqual(profile.mailing_address, "")
self.assertIsNone(profile.city) self.assertEqual(profile.city, "")
self.assertEqual(profile.country, "") self.assertEqual(profile.country, "")
self.assertIsNone(profile.goals) self.assertEqual(profile.goals, "")
self.assertEqual(profile.meta, "") self.assertEqual(
profile.get_meta(),
{
"extra1": "",
"extra2": "",
}
)
self.assertIsNone(profile.year_of_birth) self.assertIsNone(profile.year_of_birth)
@unittest.skipUnless( @unittest.skipUnless(
...@@ -267,7 +273,7 @@ class TestCreateAccountValidation(TestCase): ...@@ -267,7 +273,7 @@ class TestCreateAccountValidation(TestCase):
# Missing # Missing
del params["username"] del params["username"]
assert_username_error("Error (401 username). E-mail us.") assert_username_error("Username must be minimum of two characters long")
# Empty, too short # Empty, too short
for username in ["", "a"]: for username in ["", "a"]:
...@@ -282,10 +288,6 @@ class TestCreateAccountValidation(TestCase): ...@@ -282,10 +288,6 @@ class TestCreateAccountValidation(TestCase):
params["username"] = "invalid username" params["username"] = "invalid username"
assert_username_error("Username should only consist of A-Z and 0-9, with no spaces.") assert_username_error("Username should only consist of A-Z and 0-9, with no spaces.")
# Matching password
params["username"] = params["password"] = "test_username_and_password"
assert_username_error("Username and password fields cannot match")
def test_email(self): def test_email(self):
params = dict(self.minimal_params) params = dict(self.minimal_params)
...@@ -298,7 +300,7 @@ class TestCreateAccountValidation(TestCase): ...@@ -298,7 +300,7 @@ class TestCreateAccountValidation(TestCase):
# Missing # Missing
del params["email"] del params["email"]
assert_email_error("Error (401 email). E-mail us.") assert_email_error("A properly formatted e-mail is required")
# Empty, too short # Empty, too short
for email in ["", "a"]: for email in ["", "a"]:
...@@ -311,7 +313,7 @@ class TestCreateAccountValidation(TestCase): ...@@ -311,7 +313,7 @@ class TestCreateAccountValidation(TestCase):
# Invalid # Invalid
params["email"] = "not_an_email_address" params["email"] = "not_an_email_address"
assert_email_error("Valid e-mail is required.") assert_email_error("A properly formatted e-mail is required")
def test_password(self): def test_password(self):
params = dict(self.minimal_params) params = dict(self.minimal_params)
...@@ -325,7 +327,7 @@ class TestCreateAccountValidation(TestCase): ...@@ -325,7 +327,7 @@ class TestCreateAccountValidation(TestCase):
# Missing # Missing
del params["password"] del params["password"]
assert_password_error("Error (401 password). E-mail us.") assert_password_error("A valid password is required")
# Empty, too short # Empty, too short
for password in ["", "a"]: for password in ["", "a"]:
...@@ -334,6 +336,10 @@ class TestCreateAccountValidation(TestCase): ...@@ -334,6 +336,10 @@ class TestCreateAccountValidation(TestCase):
# Password policy is tested elsewhere # Password policy is tested elsewhere
# Matching username
params["username"] = params["password"] = "test_username_and_password"
assert_password_error("Username and password fields cannot match")
def test_name(self): def test_name(self):
params = dict(self.minimal_params) params = dict(self.minimal_params)
...@@ -346,7 +352,7 @@ class TestCreateAccountValidation(TestCase): ...@@ -346,7 +352,7 @@ class TestCreateAccountValidation(TestCase):
# Missing # Missing
del params["name"] del params["name"]
assert_name_error("Error (401 name). E-mail us.") assert_name_error("Your legal name must be a minimum of two characters long")
# Empty, too short # Empty, too short
for name in ["", "a"]: for name in ["", "a"]:
...@@ -369,13 +375,20 @@ class TestCreateAccountValidation(TestCase): ...@@ -369,13 +375,20 @@ class TestCreateAccountValidation(TestCase):
assert_honor_code_error("To enroll, you must follow the honor code.") assert_honor_code_error("To enroll, you must follow the honor code.")
# Empty, invalid # Empty, invalid
for honor_code in ["", "false", "True"]: for honor_code in ["", "false", "not_boolean"]:
params["honor_code"] = honor_code params["honor_code"] = honor_code
assert_honor_code_error("To enroll, you must follow the honor code.") assert_honor_code_error("To enroll, you must follow the honor code.")
# True
params["honor_code"] = "tRUe"
self.assert_success(params)
with override_settings(REGISTRATION_EXTRA_FIELDS={"honor_code": "optional"}): with override_settings(REGISTRATION_EXTRA_FIELDS={"honor_code": "optional"}):
# Missing # Missing
del params["honor_code"] del params["honor_code"]
# Need to change username/email because user was created above
params["username"] = "another_test_username"
params["email"] = "another_test_email@example.com"
self.assert_success(params) self.assert_success(params)
def test_terms_of_service(self): def test_terms_of_service(self):
...@@ -393,10 +406,14 @@ class TestCreateAccountValidation(TestCase): ...@@ -393,10 +406,14 @@ class TestCreateAccountValidation(TestCase):
assert_terms_of_service_error("You must accept the terms of service.") assert_terms_of_service_error("You must accept the terms of service.")
# Empty, invalid # Empty, invalid
for terms_of_service in ["", "false", "True"]: for terms_of_service in ["", "false", "not_boolean"]:
params["terms_of_service"] = terms_of_service params["terms_of_service"] = terms_of_service
assert_terms_of_service_error("You must accept the terms of service.") assert_terms_of_service_error("You must accept the terms of service.")
# True
params["terms_of_service"] = "tRUe"
self.assert_success(params)
@ddt.data( @ddt.data(
("level_of_education", 1, "A level of education is required"), ("level_of_education", 1, "A level of education is required"),
("gender", 1, "Your gender is required"), ("gender", 1, "Your gender is required"),
......
...@@ -56,7 +56,7 @@ from student.models import ( ...@@ -56,7 +56,7 @@ from student.models import (
CourseEnrollmentAllowed, UserStanding, LoginFailures, CourseEnrollmentAllowed, UserStanding, LoginFailures,
create_comments_service_user, PasswordHistory, UserSignupSource, create_comments_service_user, PasswordHistory, UserSignupSource,
DashboardConfiguration, LinkedInAddToProfileConfiguration) DashboardConfiguration, LinkedInAddToProfileConfiguration)
from student.forms import PasswordResetFormNoActive from student.forms import AccountCreationForm, PasswordResetFormNoActive
from verify_student.models import SoftwareSecurePhotoVerification, MidcourseReverificationWindow from verify_student.models import SoftwareSecurePhotoVerification, MidcourseReverificationWindow
from certificates.models import CertificateStatuses, certificate_status_for_student from certificates.models import CertificateStatuses, certificate_status_for_student
...@@ -1336,7 +1336,7 @@ def user_signup_handler(sender, **kwargs): # pylint: disable=unused-argument ...@@ -1336,7 +1336,7 @@ def user_signup_handler(sender, **kwargs): # pylint: disable=unused-argument
log.info(u'user {} originated from a white labeled "Microsite"'.format(kwargs['instance'].id)) log.info(u'user {} originated from a white labeled "Microsite"'.format(kwargs['instance'].id))
def _do_create_account(post_vars, extended_profile=None): def _do_create_account(form):
""" """
Given cleaned post variables, create the User and UserProfile objects, as well as the Given cleaned post variables, create the User and UserProfile objects, as well as the
registration for this user. registration for this user.
...@@ -1345,10 +1345,15 @@ def _do_create_account(post_vars, extended_profile=None): ...@@ -1345,10 +1345,15 @@ def _do_create_account(post_vars, extended_profile=None):
Note: this function is also used for creating test users. Note: this function is also used for creating test users.
""" """
user = User(username=post_vars['username'], if not form.is_valid():
email=post_vars['email'], raise ValidationError(form.errors)
is_active=False)
user.set_password(post_vars['password']) user = User(
username=form.cleaned_data["username"],
email=form.cleaned_data["email"],
is_active=False
)
user.set_password(form.cleaned_data["password"])
registration = Registration() registration = Registration()
# TODO: Rearrange so that if part of the process fails, the whole process fails. # TODO: Rearrange so that if part of the process fails, the whole process fails.
...@@ -1357,14 +1362,14 @@ def _do_create_account(post_vars, extended_profile=None): ...@@ -1357,14 +1362,14 @@ def _do_create_account(post_vars, extended_profile=None):
user.save() user.save()
except IntegrityError: except IntegrityError:
# Figure out the cause of the integrity error # Figure out the cause of the integrity error
if len(User.objects.filter(username=post_vars['username'])) > 0: if len(User.objects.filter(username=user.username)) > 0:
raise AccountValidationError( raise AccountValidationError(
_("An account with the Public Username '{username}' already exists.").format(username=post_vars['username']), _("An account with the Public Username '{username}' already exists.").format(username=user.username),
field="username" field="username"
) )
elif len(User.objects.filter(email=post_vars['email'])) > 0: elif len(User.objects.filter(email=user.email)) > 0:
raise AccountValidationError( raise AccountValidationError(
_("An account with the Email '{email}' already exists.").format(email=post_vars['email']), _("An account with the Email '{email}' already exists.").format(email=user.email),
field="email" field="email"
) )
else: else:
...@@ -1377,25 +1382,17 @@ def _do_create_account(post_vars, extended_profile=None): ...@@ -1377,25 +1382,17 @@ def _do_create_account(post_vars, extended_profile=None):
registration.register(user) registration.register(user)
profile = UserProfile(user=user) profile_fields = [
profile.name = post_vars['name'] "name", "level_of_education", "gender", "mailing_address", "city", "country", "goals",
profile.level_of_education = post_vars.get('level_of_education') "year_of_birth"
profile.gender = post_vars.get('gender') ]
profile.mailing_address = post_vars.get('mailing_address') profile = UserProfile(
profile.city = post_vars.get('city') user=user,
profile.country = post_vars.get('country') **{key: form.cleaned_data.get(key) for key in profile_fields}
profile.goals = post_vars.get('goals') )
extended_profile = form.cleaned_extended_profile
# add any extended profile information in the denormalized 'meta' field in the profile
if extended_profile: if extended_profile:
profile.meta = json.dumps(extended_profile) profile.meta = json.dumps(extended_profile)
try:
profile.year_of_birth = int(post_vars['year_of_birth'])
except (ValueError, KeyError):
# If they give us garbage, just ignore it instead
# of asking them to put an integer.
profile.year_of_birth = None
try: try:
profile.save() profile.save()
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
...@@ -1447,19 +1444,15 @@ def create_account(request, post_override=None): # pylint: disable-msg=too-many ...@@ -1447,19 +1444,15 @@ def create_account(request, post_override=None): # pylint: disable-msg=too-many
post_vars.update(dict(email=email, name=name, password=password)) post_vars.update(dict(email=email, name=name, password=password))
log.debug(u'In create_account with external_auth: user = %s, email=%s', name, email) log.debug(u'In create_account with external_auth: user = %s, email=%s', name, email)
# Confirm we have a properly formed request extra_fields = microsite.get_value(
for req_field in ['username', 'email', 'password', 'name']: 'REGISTRATION_EXTRA_FIELDS',
if req_field not in post_vars: getattr(settings, 'REGISTRATION_EXTRA_FIELDS', {})
js['value'] = _("Error (401 {field}). E-mail us.").format(field=req_field) )
js['field'] = req_field extended_profile_fields = microsite.get_value('extended_profile_fields', [])
return JsonResponse(js, status=400) enforce_password_policy = (
settings.FEATURES.get("ENFORCE_PASSWORD_POLICY", False) and
if extra_fields.get('honor_code', 'required') == 'required' and \ not do_external_auth
post_vars.get('honor_code', 'false') != u'true': )
js['value'] = _("To enroll, you must follow the honor code.")
js['field'] = 'honor_code'
return JsonResponse(js, status=400)
# Can't have terms of service for certain SHIB users, like at Stanford # Can't have terms of service for certain SHIB users, like at Stanford
tos_required = ( tos_required = (
not settings.FEATURES.get("AUTH_USE_SHIB") or not settings.FEATURES.get("AUTH_USE_SHIB") or
...@@ -1470,120 +1463,29 @@ def create_account(request, post_override=None): # pylint: disable-msg=too-many ...@@ -1470,120 +1463,29 @@ def create_account(request, post_override=None): # pylint: disable-msg=too-many
) )
) )
if tos_required: form = AccountCreationForm(
if post_vars.get('terms_of_service', 'false') != u'true': data=post_vars,
js['value'] = _("You must accept the terms of service.") extra_fields=extra_fields,
js['field'] = 'terms_of_service' extended_profile_fields=extended_profile_fields,
return JsonResponse(js, status=400) enforce_username_neq_password=True,
enforce_password_policy=enforce_password_policy,
# Confirm appropriate fields are there. tos_required=tos_required
# TODO: Check e-mail format is correct. )
# TODO: Confirm e-mail is not from a generic domain (mailinator, etc.)? Not sure if
# this is a good idea
# TODO: Check password is sane
required_post_vars = ['username', 'email', 'name', 'password']
required_post_vars += [fieldname for fieldname, val in extra_fields.items()
if val == 'required']
if tos_required:
required_post_vars.append('terms_of_service')
for field_name in required_post_vars:
if field_name in ('gender', 'level_of_education'):
min_length = 1
else:
min_length = 2
if field_name not in post_vars or len(post_vars[field_name]) < min_length:
error_str = {
'username': _('Username must be minimum of two characters long'),
'email': _('A properly formatted e-mail is required'),
'name': _('Your legal name must be a minimum of two characters long'),
'password': _('A valid password is required'),
'terms_of_service': _('Accepting Terms of Service is required'),
'honor_code': _('Agreeing to the Honor Code is required'),
'level_of_education': _('A level of education is required'),
'gender': _('Your gender is required'),
'year_of_birth': _('Your year of birth is required'),
'mailing_address': _('Your mailing address is required'),
'goals': _('A description of your goals is required'),
'city': _('A city is required'),
'country': _('A country is required')
}
if field_name in error_str:
js['value'] = error_str[field_name]
else:
js['value'] = _('You are missing one or more required fields')
js['field'] = field_name
return JsonResponse(js, status=400)
max_length = 75
if field_name == 'username':
max_length = 30
if field_name in ('email', 'username') and len(post_vars[field_name]) > max_length:
error_str = {
'username': _('Username cannot be more than {num} characters long').format(num=max_length),
'email': _('Email cannot be more than {num} characters long').format(num=max_length)
}
js['value'] = error_str[field_name]
js['field'] = field_name
return JsonResponse(js, status=400)
try:
validate_email(post_vars['email'])
except ValidationError:
js['value'] = _("Valid e-mail is required.")
js['field'] = 'email'
return JsonResponse(js, status=400)
try:
validate_slug(post_vars['username'])
except ValidationError:
js['value'] = _("Username should only consist of A-Z and 0-9, with no spaces.")
js['field'] = 'username'
return JsonResponse(js, status=400)
# enforce password complexity as an optional feature
# but not if we're doing ext auth b/c those pws never get used and are auto-generated so might not pass validation
if settings.FEATURES.get('ENFORCE_PASSWORD_POLICY', False) and not do_external_auth:
try:
password = post_vars['password']
validate_password_length(password) if not form.is_valid():
validate_password_complexity(password) field, error_list = next(form.errors.iteritems())
validate_password_dictionary(password) return JsonResponse(
except ValidationError, err: {
js['value'] = _('Password: ') + '; '.join(err.messages) "success": False,
js['field'] = 'password' "field": field,
return JsonResponse(js, status=400) "value": error_list[0],
},
status=400
)
# allow microsites to define 'extended profile fields' which are
# captured on user signup (for example via an overriden registration.html)
# and then stored in the UserProfile
extended_profile_fields = microsite.get_value('extended_profile_fields', [])
extended_profile = None
for field in extended_profile_fields:
if field in post_vars:
if not extended_profile:
extended_profile = {}
extended_profile[field] = post_vars[field]
# Make sure that password and username fields do not match
username = post_vars['username']
password = post_vars['password']
if username == password:
js['value'] = _("Username and password fields cannot match")
js['field'] = 'username'
return JsonResponse(js, status=400)
# Ok, looks like everything is legit. Create the account.
try: try:
with transaction.commit_on_success(): with transaction.commit_on_success():
ret = _do_create_account(post_vars, extended_profile) ret = _do_create_account(form)
except AccountValidationError as exc: except AccountValidationError as exc:
return JsonResponse({'success': False, 'value': exc.message, 'field': exc.field}, status=400) return JsonResponse({'success': False, 'value': exc.message, 'field': exc.field}, status=400)
...@@ -1757,21 +1659,21 @@ def auto_auth(request): ...@@ -1757,21 +1659,21 @@ def auto_auth(request):
role_names = [v.strip() for v in request.GET.get('roles', '').split(',') if v.strip()] role_names = [v.strip() for v in request.GET.get('roles', '').split(',') if v.strip()]
login_when_done = 'no_login' not in request.GET login_when_done = 'no_login' not in request.GET
# Get or create the user object form = AccountCreationForm(
post_data = { data={
'username': username, 'username': username,
'email': email, 'email': email,
'password': password, 'password': password,
'name': full_name, 'name': full_name,
'honor_code': u'true', },
'terms_of_service': u'true', tos_required=False
} )
# Attempt to create the account. # Attempt to create the account.
# If successful, this will return a tuple containing # If successful, this will return a tuple containing
# the new user object. # the new user object.
try: try:
user, _profile, reg = _do_create_account(post_data) user, _profile, reg = _do_create_account(form)
except AccountValidationError: except AccountValidationError:
# Attempt to retrieve the existing user. # Attempt to retrieve the existing user.
user = User.objects.get(username=username) user = User.objects.get(username=username)
......
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