From 161b94ef7bbdba2f8106871be796f5d703727a3f Mon Sep 17 00:00:00 2001 From: Chris Beaven Date: Mon, 9 May 2011 01:10:40 +0000 Subject: [PATCH] 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 --- django/contrib/auth/management/__init__.py | 55 ++++++++++++++++++- .../management/commands/createsuperuser.py | 21 +------ django/contrib/auth/tests/__init__.py | 1 + django/contrib/auth/tests/management.py | 27 +++++++++ 4 files changed, 84 insertions(+), 20 deletions(-) create mode 100644 django/contrib/auth/tests/management.py diff --git a/django/contrib/auth/management/__init__.py b/django/contrib/auth/management/__init__.py index 46dbea83f3..9966849ca9 100644 --- a/django/contrib/auth/management/__init__.py +++ b/django/contrib/auth/management/__init__.py @@ -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, diff --git a/django/contrib/auth/management/commands/createsuperuser.py b/django/contrib/auth/management/commands/createsuperuser.py index bcccebe4c3..b3a3479c28 100644 --- a/django/contrib/auth/management/commands/createsuperuser.py +++ b/django/contrib/auth/management/commands/createsuperuser.py @@ -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 diff --git a/django/contrib/auth/tests/__init__.py b/django/contrib/auth/tests/__init__.py index 3a8f55b6e7..2d7d0fa1a1 100644 --- a/django/contrib/auth/tests/__init__.py +++ b/django/contrib/auth/tests/__init__.py @@ -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 diff --git a/django/contrib/auth/tests/management.py b/django/contrib/auth/tests/management.py new file mode 100644 index 0000000000..0af68736ff --- /dev/null +++ b/django/contrib/auth/tests/management.py @@ -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')