Modified auth management commands to handle custom user definitions.

This commit is contained in:
Russell Keith-Magee 2012-06-04 20:10:51 +08:00
parent 7cc0baf89d
commit dabe362836
13 changed files with 357 additions and 186 deletions

View File

@ -4,6 +4,7 @@ from django.contrib.admin.forms import AdminAuthenticationForm
from django.contrib.auth.views import login
from django.contrib.auth import REDIRECT_FIELD_NAME
def staff_member_required(view_func):
"""
Decorator for views that checks that the user is logged in and is a staff

View File

@ -64,16 +64,16 @@ class UserCreationForm(forms.ModelForm):
}
username = forms.RegexField(label=_("Username"), max_length=30,
regex=r'^[\w.@+-]+$',
help_text = _("Required. 30 characters or fewer. Letters, digits and "
help_text=_("Required. 30 characters or fewer. Letters, digits and "
"@/./+/-/_ only."),
error_messages = {
error_messages={
'invalid': _("This value may contain only letters, numbers and "
"@/./+/-/_ characters.")})
password1 = forms.CharField(label=_("Password"),
widget=forms.PasswordInput)
password2 = forms.CharField(label=_("Password confirmation"),
widget=forms.PasswordInput,
help_text = _("Enter the same password as above, for verification."))
help_text=_("Enter the same password as above, for verification."))
class Meta:
model = User
@ -108,9 +108,9 @@ class UserCreationForm(forms.ModelForm):
class UserChangeForm(forms.ModelForm):
username = forms.RegexField(
label=_("Username"), max_length=30, regex=r"^[\w.@+-]+$",
help_text = _("Required. 30 characters or fewer. Letters, digits and "
help_text=_("Required. 30 characters or fewer. Letters, digits and "
"@/./+/-/_ only."),
error_messages = {
error_messages={
'invalid': _("This value may contain only letters, numbers and "
"@/./+/-/_ characters.")})
password = ReadOnlyPasswordHashField(label=_("Password"),

View File

@ -4,9 +4,11 @@ Creates permissions for all installed apps that need permissions.
import getpass
import locale
import unicodedata
from django.contrib.auth import models as auth_app
from django.core import exceptions
from django.db.models import get_models, signals
from django.contrib.auth.models import User
from django.contrib.auth.models import User, get_user_model
def _get_permission_codename(action, opts):
@ -60,7 +62,9 @@ def create_permissions(app, created_models, verbosity, **kwargs):
def create_superuser(app, created_models, verbosity, db, **kwargs):
from django.core.management import call_command
if auth_app.User in created_models and kwargs.get('interactive', True):
UserModel = get_user_model()
if UserModel in created_models and kwargs.get('interactive', True):
msg = ("\nYou just installed Django's auth system, which means you "
"don't have any superusers defined.\nWould you like to create one "
"now? (yes/no): ")
@ -100,16 +104,24 @@ def get_default_username(check_db=True):
:returns: The username, or an empty string if no username can be
determined.
"""
from django.contrib.auth.management.commands.createsuperuser import (
RE_VALID_USERNAME)
# If the User model has been swapped out, we can't make any assumptions
# about the default user name.
if User._meta.swapped:
return ''
default_username = get_system_username()
try:
default_username = unicodedata.normalize('NFKD', default_username)\
.encode('ascii', 'ignore').replace(' ', '').lower()
except UnicodeDecodeError:
return ''
if not RE_VALID_USERNAME.match(default_username):
# Run the username validator
try:
User._meta.get_field('username').run_validators(default_username)
except exceptions.ValidationError:
return ''
# Don't return the default username if it is already taken.
if check_db and default_username:
try:
@ -120,8 +132,7 @@ def get_default_username(check_db=True):
return ''
return default_username
signals.post_syncdb.connect(create_permissions,
dispatch_uid = "django.contrib.auth.management.create_permissions")
dispatch_uid="django.contrib.auth.management.create_permissions")
signals.post_syncdb.connect(create_superuser,
sender=auth_app, dispatch_uid = "django.contrib.auth.management.create_superuser")
sender=auth_app, dispatch_uid="django.contrib.auth.management.create_superuser")

View File

@ -2,7 +2,7 @@ import getpass
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import User
from django.contrib.auth.models import get_user_model
from django.db import DEFAULT_DB_ALIAS
@ -30,12 +30,16 @@ class Command(BaseCommand):
else:
username = getpass.getuser()
UserModel = get_user_model()
try:
u = User.objects.using(options.get('database')).get(username=username)
except User.DoesNotExist:
u = UserModel.objects.using(options.get('database')).get(**{
getattr(UserModel, 'USERNAME_FIELD', 'username'): username
})
except UserModel.DoesNotExist:
raise CommandError("user '%s' does not exist" % username)
self.stdout.write("Changing password for user '%s'\n" % u.username)
self.stdout.write("Changing password for user '%s'\n" % u)
MAX_TRIES = 3
count = 0
@ -48,9 +52,9 @@ class Command(BaseCommand):
count = count + 1
if count == MAX_TRIES:
raise CommandError("Aborting password change for user '%s' after %s attempts" % (username, count))
raise CommandError("Aborting password change for user '%s' after %s attempts" % (u, count))
u.set_password(p1)
u.save()
return "Password changed successfully for user '%s'" % u.username
return "Password changed successfully for user '%s'" % u

View File

@ -3,108 +3,113 @@ Management utility to create superusers.
"""
import getpass
import re
import sys
from optparse import make_option
from django.contrib.auth.models import User
from django.contrib.auth.management import get_default_username
from django.contrib.auth.models import get_user_model
from django.core import exceptions
from django.core.management.base import BaseCommand, CommandError
from django.db import DEFAULT_DB_ALIAS
from django.utils.translation import ugettext as _
RE_VALID_USERNAME = re.compile('[\w.@+-]+$')
EMAIL_RE = re.compile(
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"' # quoted-string
r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
def is_valid_email(value):
if not EMAIL_RE.search(value):
raise exceptions.ValidationError(_('Enter a valid e-mail address.'))
from django.utils.text import capfirst
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('--username', dest='username', default=None,
help='Specifies the username for the superuser.'),
make_option('--email', dest='email', default=None,
help='Specifies the email address for the superuser.'),
make_option('--noinput', action='store_false', dest='interactive', default=True,
help=('Tells Django to NOT prompt the user for input of any kind. '
'You must use --username and --email with --noinput, and '
'superusers created with --noinput will not be able to log '
'in until they\'re given a valid password.')),
'You must use --username with --noinput, along with an option for '
'any other required field. Superusers created with --noinput will '
' not be able to log in until they\'re given a valid password.')),
make_option('--database', action='store', dest='database',
default=DEFAULT_DB_ALIAS, help='Specifies the database to use. Default is "default".'),
) + tuple(
make_option('--%s' % field, dest=field, default=None,
help='Specifies the %s for the superuser.' % field)
for field in getattr(get_user_model(), 'REQUIRED_FIELDS', ['email'])
)
help = 'Used to create a superuser.'
def handle(self, *args, **options):
username = options.get('username', None)
email = options.get('email', None)
interactive = options.get('interactive')
verbosity = int(options.get('verbosity', 1))
database = options.get('database')
# Do quick and dirty validation if --noinput
if not interactive:
if not username or not email:
raise CommandError("You must use --username and --email with --noinput.")
if not RE_VALID_USERNAME.match(username):
raise CommandError("Invalid username. Use only letters, digits, and underscores")
try:
is_valid_email(email)
except exceptions.ValidationError:
raise CommandError("Invalid email address.")
UserModel = get_user_model()
username_field = UserModel._meta.get_field(getattr(UserModel, 'USERNAME_FIELD', 'username'))
other_fields = getattr(UserModel, 'REQUIRED_FIELDS', ['email'])
# If not provided, create the user with an unusable password
password = None
other_data = {}
# Prompt for username/email/password. Enclose this whole thing in a
# try/except to trap for a keyboard interrupt and exit gracefully.
if interactive:
# Do quick and dirty validation if --noinput
if not interactive:
try:
if not username:
raise CommandError("You must use --username with --noinput.")
username = username_field.clean(username, None)
for field_name in other_fields:
if options.get(field_name):
field = UserModel._meta.get_field(field_name)
other_data[field_name] = field.clean(options[field_name], None)
else:
raise CommandError("You must use --%s with --noinput." % field_name)
except exceptions.ValidationError, e:
raise CommandError('; '.join(e.messages))
else:
# Prompt for username/password, and any other required fields.
# Enclose this whole thing in a try/except to trap for a
# keyboard interrupt and exit gracefully.
default_username = get_default_username()
try:
# Get a username
while 1:
while username is None:
username_field = UserModel._meta.get_field(getattr(UserModel, 'USERNAME_FIELD', 'username'))
if not username:
input_msg = 'Username'
input_msg = capfirst(username_field.verbose_name)
if default_username:
input_msg += ' (leave blank to use %r)' % default_username
username = raw_input(input_msg + ': ')
if default_username and username == '':
raw_value = raw_input(input_msg + ': ')
if default_username and raw_value == '':
username = default_username
if not RE_VALID_USERNAME.match(username):
self.stderr.write("Error: That username is invalid. Use only letters, digits and underscores.")
try:
username = username_field.clean(raw_value, None)
except exceptions.ValidationError, e:
self.stderr.write("Error: %s" % '; '.join(e.messages))
username = None
continue
try:
User.objects.using(database).get(username=username)
except User.DoesNotExist:
break
UserModel.objects.using(database).get(**{
getattr(UserModel, 'USERNAME_FIELD', 'username'): username
})
except UserModel.DoesNotExist:
pass
else:
self.stderr.write("Error: That username is already taken.")
username = None
# Get an email
while 1:
if not email:
email = raw_input('E-mail address: ')
try:
is_valid_email(email)
except exceptions.ValidationError:
self.stderr.write("Error: That e-mail address is invalid.")
email = None
else:
break
for field_name in other_fields:
field = UserModel._meta.get_field(field_name)
other_data[field_name] = None
while other_data[field_name] is None:
raw_value = raw_input(capfirst(field.verbose_name + ': '))
try:
other_data[field_name] = field.clean(raw_value, None)
except exceptions.ValidationError, e:
self.stderr.write("Error: %s" % '; '.join(e.messages))
other_data[field_name] = None
# Get a password
while 1:
while password is None:
if not password:
password = getpass.getpass()
password2 = getpass.getpass('Password (again): ')
@ -116,12 +121,11 @@ class Command(BaseCommand):
self.stderr.write("Error: Blank passwords aren't allowed.")
password = None
continue
break
except KeyboardInterrupt:
self.stderr.write("\nOperation cancelled.")
sys.exit(1)
User.objects.db_manager(database).create_superuser(username, email, password)
UserModel.objects.db_manager(database).create_superuser(username=username, password=password, **other_data)
if verbosity >= 1:
self.stdout.write("Superuser created successfully.")
self.stdout.write("Superuser created successfully.")

View File

@ -1,7 +1,10 @@
import re
import urllib
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.mail import send_mail
from django.core import validators
from django.db import models
from django.db.models.manager import EmptyManager
from django.utils.crypto import get_random_string
@ -128,7 +131,7 @@ class Group(models.Model):
return (self.name,)
class UserManager(models.Manager):
class BaseUserManager(models.Manager):
@classmethod
def normalize_email(cls, email):
@ -145,6 +148,24 @@ class UserManager(models.Manager):
email = '@'.join([email_name, domain_part.lower()])
return email
def make_random_password(self, length=10,
allowed_chars='abcdefghjkmnpqrstuvwxyz'
'ABCDEFGHJKLMNPQRSTUVWXYZ'
'23456789'):
"""
Generates a random password with the given length and given
allowed_chars. Note that the default value of allowed_chars does not
have "I" or "O" or letters and digits that look similar -- just to
avoid confusion.
"""
return get_random_string(length, allowed_chars)
def get_by_natural_key(self, username):
return self.get(**{getattr(self, 'USERNAME_FIELD', 'username'): username})
class UserManager(BaseUserManager):
def create_user(self, username, email=None, password=None):
"""
Creates and saves a User with the given username, email and password.
@ -169,21 +190,6 @@ class UserManager(models.Manager):
u.save(using=self._db)
return u
def make_random_password(self, length=10,
allowed_chars='abcdefghjkmnpqrstuvwxyz'
'ABCDEFGHJKLMNPQRSTUVWXYZ'
'23456789'):
"""
Generates a random password with the given length and given
allowed_chars. Note that the default value of allowed_chars does not
have "I" or "O" or letters and digits that look similar -- just to
avoid confusion.
"""
return get_random_string(length, allowed_chars)
def get_by_natural_key(self, username):
return self.get(username=username)
# A few helper functions for common logic between User and AnonymousUser.
def _user_get_all_permissions(user, obj):
@ -219,6 +225,7 @@ def _user_has_module_perms(user, app_label):
class AbstractBaseUser(models.Model):
password = models.CharField(_('password'), max_length=128)
last_login = models.DateTimeField(_('last login'), default=timezone.now)
class Meta:
abstract = True
@ -264,6 +271,12 @@ class AbstractBaseUser(models.Model):
raise NotImplementedError()
def get_user_model():
"Return the User model that is active in this project"
app_label, model_name = settings.AUTH_USER_MODEL.split('.')
return models.get_model(app_label, model_name)
class User(AbstractBaseUser):
"""
Users within the Django authentication system are represented by this
@ -273,10 +286,13 @@ class User(AbstractBaseUser):
"""
username = models.CharField(_('username'), max_length=30, unique=True,
help_text=_('Required. 30 characters or fewer. Letters, numbers and '
'@/./+/-/_ characters'))
'@/./+/-/_ characters'),
validators=[
validators.RegexValidator(re.compile('^[\w.@+-]+$'), _(u'Enter a valid username.'), 'invalid')
])
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
email = models.EmailField(_('e-mail address'), blank=True)
email = models.EmailField(_('email address'), blank=True)
is_staff = models.BooleanField(_('staff status'), default=False,
help_text=_('Designates whether the user can log into this admin '
'site.'))
@ -286,7 +302,6 @@ class User(AbstractBaseUser):
is_superuser = models.BooleanField(_('superuser status'), default=False,
help_text=_('Designates that this user has all permissions without '
'explicitly assigning them.'))
last_login = models.DateTimeField(_('last login'), default=timezone.now)
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
groups = models.ManyToManyField(Group, verbose_name=_('groups'),
blank=True, help_text=_('The groups this user belongs to. A user will '
@ -295,6 +310,7 @@ class User(AbstractBaseUser):
user_permissions = models.ManyToManyField(Permission,
verbose_name=_('user permissions'), blank=True,
help_text='Specific permissions for this user.')
objects = UserManager()
class Meta:
@ -318,6 +334,10 @@ class User(AbstractBaseUser):
full_name = u'%s %s' % (self.first_name, self.last_name)
return full_name.strip()
def get_short_name(self):
"Returns the short name for the user."
return self.first_name
def get_group_permissions(self, obj=None):
"""
Returns a list of permission strings that this user has through his/her

View File

@ -1,26 +1,15 @@
from django.contrib.auth.tests.auth_backends import (BackendTest,
RowlevelBackendTest, AnonymousUserBackendTest, NoBackendsTest,
InActiveUserBackendTest)
from django.contrib.auth.tests.basic import BasicTestCase
from django.contrib.auth.tests.context_processors import AuthContextProcessorTests
from django.contrib.auth.tests.decorators import LoginRequiredTestCase
from django.contrib.auth.tests.forms import (UserCreationFormTest,
AuthenticationFormTest, SetPasswordFormTest, PasswordChangeFormTest,
UserChangeFormTest, PasswordResetFormTest)
from django.contrib.auth.tests.remote_user import (RemoteUserTest,
RemoteUserNoCreateTest, RemoteUserCustomTest)
from django.contrib.auth.tests.management import (
GetDefaultUsernameTestCase,
ChangepasswordManagementCommandTestCase,
)
from django.contrib.auth.tests.models import (ProfileTestCase, NaturalKeysTestCase,
LoadDataWithoutNaturalKeysTestCase, LoadDataWithNaturalKeysTestCase,
UserManagerTestCase)
from django.contrib.auth.tests.hashers import TestUtilsHashPass
from django.contrib.auth.tests.signals import SignalTestCase
from django.contrib.auth.tests.tokens import TokenGeneratorTest
from django.contrib.auth.tests.views import (AuthViewNamedURLTests,
PasswordResetTest, ChangePasswordTest, LoginTest, LogoutTest,
LoginURLSettings)
from django.contrib.auth.tests.custom_user import *
from django.contrib.auth.tests.auth_backends import *
from django.contrib.auth.tests.basic import *
from django.contrib.auth.tests.context_processors import *
from django.contrib.auth.tests.decorators import *
from django.contrib.auth.tests.forms import *
from django.contrib.auth.tests.remote_user import *
from django.contrib.auth.tests.management import *
from django.contrib.auth.tests.models import *
from django.contrib.auth.tests.hashers import *
from django.contrib.auth.tests.signals import *
from django.contrib.auth.tests.tokens import *
from django.contrib.auth.tests.views import *
# The password for the fixture data users is 'password'

View File

@ -1,14 +1,8 @@
from django.test import TestCase
from django.utils.unittest import skipUnless
from django.contrib.auth.models import User, AnonymousUser
from django.core.management import call_command
from StringIO import StringIO
try:
import crypt as crypt_module
except ImportError:
crypt_module = None
class BasicTestCase(TestCase):
def test_user(self):
@ -35,7 +29,7 @@ class BasicTestCase(TestCase):
self.assertFalse(u.is_superuser)
# Check API-based user creation with no password
u2 = User.objects.create_user('testuser2', 'test2@example.com')
User.objects.create_user('testuser2', 'test2@example.com')
self.assertFalse(u.has_usable_password())
def test_user_no_email(self):
@ -66,48 +60,3 @@ class BasicTestCase(TestCase):
self.assertTrue(super.is_superuser)
self.assertTrue(super.is_active)
self.assertTrue(super.is_staff)
def test_createsuperuser_management_command(self):
"Check the operation of the createsuperuser management command"
# We can use the management command to create a superuser
new_io = StringIO()
call_command("createsuperuser",
interactive=False,
username="joe",
email="joe@somewhere.org",
stdout=new_io
)
command_output = new_io.getvalue().strip()
self.assertEqual(command_output, 'Superuser created successfully.')
u = User.objects.get(username="joe")
self.assertEqual(u.email, 'joe@somewhere.org')
# created password should be unusable
self.assertFalse(u.has_usable_password())
# We can supress output on the management command
new_io = StringIO()
call_command("createsuperuser",
interactive=False,
username="joe2",
email="joe2@somewhere.org",
verbosity=0,
stdout=new_io
)
command_output = new_io.getvalue().strip()
self.assertEqual(command_output, '')
u = User.objects.get(username="joe2")
self.assertEqual(u.email, 'joe2@somewhere.org')
self.assertFalse(u.has_usable_password())
new_io = StringIO()
call_command("createsuperuser",
interactive=False,
username="joe+admin@somewhere.org",
email="joe@somewhere.org",
stdout=new_io
)
u = User.objects.get(username="joe+admin@somewhere.org")
self.assertEqual(u.email, 'joe@somewhere.org')
self.assertFalse(u.has_usable_password())

View File

@ -0,0 +1,78 @@
# The custom User uses email as the unique identifier, and requires
# that every user provide a date of birth. This lets us test
# changes in username datatype, and non-text required fields.
from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
class CustomUserManager(BaseUserManager):
def create_user(self, email, date_of_birth, password=None):
"""
Creates and saves a User with the given email and password.
"""
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=CustomUserManager.normalize_email(email),
date_of_birth=date_of_birth,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, username, password, date_of_birth):
u = self.create_user(username, password=password, date_of_birth=date_of_birth)
u.is_admin = True
u.save(using=self._db)
return u
class CustomUser(AbstractBaseUser):
email = models.EmailField(verbose_name='email address', max_length=255, unique=True)
is_admin = models.BooleanField(default=False)
date_of_birth = models.DateField()
objects = CustomUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['date_of_birth']
class Meta:
app_label = 'auth'
def get_full_name(self):
return self.email
def get_short_name(self):
return self.email
def __unicode__(self):
return self.email
# Maybe required?
def get_group_permissions(self, obj=None):
return set()
def get_all_permissions(self, obj=None):
return set()
def has_perm(self, perm, obj=None):
return True
def has_perms(self, perm_list, obj=None):
return True
def has_module_perms(self, app_label):
return True
# Admin required fields
@property
def is_staff(self):
return self.is_admin
@property
def is_active(self):
return True

View File

@ -1,9 +1,14 @@
from datetime import date
from StringIO import StringIO
from django.contrib.auth import models, management
from django.contrib.auth.management.commands import changepassword
from django.contrib.auth.models import User
from django.contrib.auth.tests import CustomUser
from django.core.management import call_command
from django.core.management.base import CommandError
from django.test import TestCase
from django.test.utils import override_settings
class GetDefaultUsernameTestCase(TestCase):
@ -43,7 +48,7 @@ class ChangepasswordManagementCommandTestCase(TestCase):
self.stderr.close()
def test_that_changepassword_command_changes_joes_password(self):
" Executing the changepassword management command should change joe's password "
"Executing the changepassword management command should change joe's password"
self.assertTrue(self.user.check_password('qwerty'))
command = changepassword.Command()
command._get_pass = lambda *args: 'not qwerty'
@ -64,3 +69,92 @@ class ChangepasswordManagementCommandTestCase(TestCase):
with self.assertRaises(CommandError):
command.execute("joe", stdout=self.stdout, stderr=self.stderr)
class CreatesuperuserManagementCommandTestCase(TestCase):
def test_createsuperuser(self):
"Check the operation of the createsuperuser management command"
# We can use the management command to create a superuser
new_io = StringIO()
call_command("createsuperuser",
interactive=False,
username="joe",
email="joe@somewhere.org",
stdout=new_io
)
command_output = new_io.getvalue().strip()
self.assertEqual(command_output, 'Superuser created successfully.')
u = User.objects.get(username="joe")
self.assertEqual(u.email, 'joe@somewhere.org')
# created password should be unusable
self.assertFalse(u.has_usable_password())
def test_verbosity_zero(self):
# We can supress output on the management command
new_io = StringIO()
call_command("createsuperuser",
interactive=False,
username="joe2",
email="joe2@somewhere.org",
verbosity=0,
stdout=new_io
)
command_output = new_io.getvalue().strip()
self.assertEqual(command_output, '')
u = User.objects.get(username="joe2")
self.assertEqual(u.email, 'joe2@somewhere.org')
self.assertFalse(u.has_usable_password())
def test_email_in_username(self):
new_io = StringIO()
call_command("createsuperuser",
interactive=False,
username="joe+admin@somewhere.org",
email="joe@somewhere.org",
stdout=new_io
)
u = User.objects.get(username="joe+admin@somewhere.org")
self.assertEqual(u.email, 'joe@somewhere.org')
self.assertFalse(u.has_usable_password())
@override_settings(AUTH_USER_MODEL='auth.CustomUser')
def test_swappable_user(self):
"A superuser can be created when a custom User model is in use"
# We can use the management command to create a superuser
# We skip validation because the temporary substitution of the
# swappable User model messes with validation.
new_io = StringIO()
call_command("createsuperuser",
interactive=False,
username="joe@somewhere.org",
date_of_birth="1976-04-01",
stdout=new_io,
skip_validation=True
)
command_output = new_io.getvalue().strip()
self.assertEqual(command_output, 'Superuser created successfully.')
u = CustomUser.objects.get(email="joe@somewhere.org")
self.assertEqual(u.date_of_birth, date(1976, 4, 1))
# created password should be unusable
self.assertFalse(u.has_usable_password())
@override_settings(AUTH_USER_MODEL='auth.CustomUser')
def test_swappable_user_missing_required_field(self):
"A superuser can be created when a custom User model is in use"
# We can use the management command to create a superuser
# We skip validation because the temporary substitution of the
# swappable User model messes with validation.
new_io = StringIO()
with self.assertRaises(CommandError):
call_command("createsuperuser",
interactive=False,
username="joe@somewhere.org",
stdout=new_io,
stderr=new_io,
skip_validation=True
)
self.assertEqual(CustomUser.objects.count(), 0)

View File

@ -3,6 +3,7 @@ from django.conf import settings
from django.utils.http import int_to_base36, base36_to_int
from django.utils.crypto import constant_time_compare, salted_hmac
class PasswordResetTokenGenerator(object):
"""
Strategy object used to generate and check tokens for the password

View File

@ -3,42 +3,54 @@ Global Django exception and warning classes.
"""
from functools import reduce
class DjangoRuntimeWarning(RuntimeWarning):
pass
class ObjectDoesNotExist(Exception):
"The requested object does not exist"
silent_variable_failure = True
class MultipleObjectsReturned(Exception):
"The query returned multiple objects when only one was expected."
pass
class SuspiciousOperation(Exception):
"The user did something suspicious"
pass
class PermissionDenied(Exception):
"The user did not have permission to do that"
pass
class ViewDoesNotExist(Exception):
"The requested view does not exist"
pass
class MiddlewareNotUsed(Exception):
"This middleware is not used in this server configuration"
pass
class ImproperlyConfigured(Exception):
"Django is somehow improperly configured"
pass
class FieldError(Exception):
"""Some kind of problem with a model field."""
pass
NON_FIELD_ERRORS = '__all__'
class ValidationError(Exception):
"""An error while validating data."""
def __init__(self, message, code=None, params=None):
@ -85,4 +97,3 @@ class ValidationError(Exception):
else:
error_dict[NON_FIELD_ERRORS] = self.messages
return error_dict

View File

@ -9,6 +9,7 @@ from django.utils.ipv6 import is_valid_ipv6_address
# These values, if given to validate(), will trigger the self.required check.
EMPTY_VALUES = (None, '', [], (), {})
class RegexValidator(object):
regex = ''
message = _(u'Enter a valid value.')
@ -33,13 +34,14 @@ class RegexValidator(object):
if not self.regex.search(smart_unicode(value)):
raise ValidationError(self.message, code=self.code)
class URLValidator(RegexValidator):
regex = re.compile(
r'^(?:http|ftp)s?://' # http:// or https://
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' #domain...
r'localhost|' #localhost...
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
r'(?::\d+)?' # optional port
r'^(?:http|ftp)s?://' # http:// or https://
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain...
r'localhost|' # localhost...
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
r'(?::\d+)?' # optional port
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
def __call__(self, value):
@ -51,8 +53,8 @@ class URLValidator(RegexValidator):
value = smart_unicode(value)
scheme, netloc, path, query, fragment = urlparse.urlsplit(value)
try:
netloc = netloc.encode('idna') # IDN -> ACE
except UnicodeError: # invalid domain part
netloc = netloc.encode('idna') # IDN -> ACE
except UnicodeError: # invalid domain part
raise e
url = urlparse.urlunsplit((scheme, netloc, path, query, fragment))
super(URLValidator, self).__call__(url)
@ -68,6 +70,7 @@ def validate_integer(value):
except (ValueError, TypeError):
raise ValidationError('')
class EmailValidator(RegexValidator):
def __call__(self, value):
@ -99,10 +102,12 @@ validate_slug = RegexValidator(slug_re, _(u"Enter a valid 'slug' consisting of l
ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')
validate_ipv4_address = RegexValidator(ipv4_re, _(u'Enter a valid IPv4 address.'), 'invalid')
def validate_ipv6_address(value):
if not is_valid_ipv6_address(value):
raise ValidationError(_(u'Enter a valid IPv6 address.'), code='invalid')
def validate_ipv46_address(value):
try:
validate_ipv4_address(value)
@ -118,6 +123,7 @@ ip_address_validator_map = {
'ipv6': ([validate_ipv6_address], _('Enter a valid IPv6 address.')),
}
def ip_address_validators(protocol, unpack_ipv4):
"""
Depending on the given parameters returns the appropriate validators for
@ -140,7 +146,7 @@ validate_comma_separated_integer_list = RegexValidator(comma_separated_int_list_
class BaseValidator(object):
compare = lambda self, a, b: a is not b
clean = lambda self, x: x
clean = lambda self, x: x
message = _(u'Ensure this value is %(limit_value)s (it is %(show_value)s).')
code = 'limit_value'
@ -157,25 +163,28 @@ class BaseValidator(object):
params=params,
)
class MaxValueValidator(BaseValidator):
compare = lambda self, a, b: a > b
message = _(u'Ensure this value is less than or equal to %(limit_value)s.')
code = 'max_value'
class MinValueValidator(BaseValidator):
compare = lambda self, a, b: a < b
message = _(u'Ensure this value is greater than or equal to %(limit_value)s.')
code = 'min_value'
class MinLengthValidator(BaseValidator):
compare = lambda self, a, b: a < b
clean = lambda self, x: len(x)
clean = lambda self, x: len(x)
message = _(u'Ensure this value has at least %(limit_value)d characters (it has %(show_value)d).')
code = 'min_length'
class MaxLengthValidator(BaseValidator):
compare = lambda self, a, b: a > b
clean = lambda self, x: len(x)
clean = lambda self, x: len(x)
message = _(u'Ensure this value has at most %(limit_value)d characters (it has %(show_value)d).')
code = 'max_length'