Modified auth management commands to handle custom user definitions.
This commit is contained in:
parent
7cc0baf89d
commit
dabe362836
|
@ -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
|
||||
|
|
|
@ -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,7 +132,6 @@ 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")
|
||||
signals.post_syncdb.connect(create_superuser,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: ')
|
||||
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:
|
||||
is_valid_email(email)
|
||||
except exceptions.ValidationError:
|
||||
self.stderr.write("Error: That e-mail address is invalid.")
|
||||
email = None
|
||||
else:
|
||||
break
|
||||
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.")
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
|
@ -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):
|
||||
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,6 +34,7 @@ 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://
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
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)
|
||||
message = _(u'Ensure this value has at most %(limit_value)d characters (it has %(show_value)d).')
|
||||
code = 'max_length'
|
||||
|
||||
|
|
Loading…
Reference in New Issue