Fixes #15778 -- createsuperuser fails on international characters in system user names. Thanks for the patch, Hynek Cernoch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16182 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Chris Beaven 2011-05-09 01:10:40 +00:00
parent 688abe3b8d
commit 161b94ef7b
4 changed files with 84 additions and 20 deletions

View File

@ -1,14 +1,18 @@
"""
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.db.models import get_models, signals
from django.contrib.auth.models import User
def _get_permission_codename(action, opts):
return u'%s_%s' % (action, opts.object_name.lower())
def _get_all_permissions(opts):
"Returns (codename, name) for all permissions in the given opts."
perms = []
@ -16,6 +20,7 @@ def _get_all_permissions(opts):
perms.append((_get_permission_codename(action, opts), u'Can %s %s' % (action, opts.verbose_name_raw)))
return perms + list(opts.permissions)
def create_permissions(app, created_models, verbosity, **kwargs):
from django.contrib.contenttypes.models import ContentType
@ -70,6 +75,54 @@ def create_superuser(app, created_models, verbosity, **kwargs):
call_command("createsuperuser", interactive=True)
break
def get_system_username():
"""
Try to determine the current system user's username.
:returns: The username as a unicode string, or an empty string if the
username could not be determined.
"""
try:
return getpass.getuser().decode(locale.getdefaultlocale()[1])
except (ImportError, KeyError, UnicodeDecodeError):
# KeyError will be raised by os.getpwuid() (called by getuser())
# if there is no corresponding entry in the /etc/passwd file
# (a very restricted chroot environment, for example).
# UnicodeDecodeError - preventive treatment for non-latin Windows.
return u''
def get_default_username(check_db=True):
"""
Try to determine the current system user's username to use as a default.
:param check_db: If ``True``, requires that the username does not match an
existing ``auth.User`` (otherwise returns an empty string).
:returns: The username, or an empty string if no username can be
determined.
"""
from django.contrib.auth.management.commands.createsuperuser import \
RE_VALID_USERNAME
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):
return ''
# Don't return the default username if it is already taken.
if check_db and default_username:
try:
User.objects.get(username=default_username)
except User.DoesNotExist:
pass
else:
return ''
return default_username
signals.post_syncdb.connect(create_permissions,
dispatch_uid = "django.contrib.auth.management.create_permissions")
signals.post_syncdb.connect(create_superuser,

View File

@ -7,6 +7,7 @@ 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.core import exceptions
from django.core.management.base import BaseCommand, CommandError
from django.utils.translation import ugettext as _
@ -56,28 +57,10 @@ class Command(BaseCommand):
# If not provided, create the user with an unusable password
password = None
# Try to determine the current system user's username to use as a default.
try:
default_username = getpass.getuser().replace(' ', '').lower()
except (ImportError, KeyError):
# KeyError will be raised by os.getpwuid() (called by getuser())
# if there is no corresponding entry in the /etc/passwd file
# (a very restricted chroot environment, for example).
default_username = ''
# Determine whether the default username is taken, so we don't display
# it as an option.
if default_username:
try:
User.objects.get(username=default_username)
except User.DoesNotExist:
pass
else:
default_username = ''
# Prompt for username/email/password. Enclose this whole thing in a
# try/except to trap for a keyboard interrupt and exit gracefully.
if interactive:
default_username = get_default_username()
try:
# Get a username

View File

@ -8,6 +8,7 @@ from django.contrib.auth.tests.forms import (UserCreationFormTest,
UserChangeFormTest, PasswordResetFormTest)
from django.contrib.auth.tests.remote_user import (RemoteUserTest,
RemoteUserNoCreateTest, RemoteUserCustomTest)
from django.contrib.auth.tests.management import GetDefaultUsernameTestCase
from django.contrib.auth.tests.models import ProfileTestCase
from django.contrib.auth.tests.signals import SignalTestCase
from django.contrib.auth.tests.tokens import TokenGeneratorTest

View File

@ -0,0 +1,27 @@
from django.test import TestCase
from django.contrib.auth import models, management
class GetDefaultUsernameTestCase(TestCase):
def setUp(self):
self._getpass_getuser = management.get_system_username
def tearDown(self):
management.get_system_username = self._getpass_getuser
def test_simple(self):
management.get_system_username = lambda: u'joe'
self.assertEqual(management.get_default_username(), 'joe')
def test_existing(self):
models.User.objects.create(username='joe')
management.get_system_username = lambda: u'joe'
self.assertEqual(management.get_default_username(), '')
self.assertEqual(
management.get_default_username(check_db=False), 'joe')
def test_i18n(self):
# 'Julia' with accented 'u':
management.get_system_username = lambda: u'J\xfalia'
self.assertEqual(management.get_default_username(), 'julia')