diff --git a/django/contrib/auth/create_superuser.py b/django/contrib/auth/create_superuser.py index 7b6cefd268..8d13a7916a 100644 --- a/django/contrib/auth/create_superuser.py +++ b/django/contrib/auth/create_superuser.py @@ -1,94 +1,8 @@ """ -Helper function for creating superusers in the authentication system. - -If run from the command line, this module lets you create a superuser -interactively. +Create a superuser from the command line. Deprecated; use manage.py +createsuperuser instead. """ -from django.core import validators -from django.contrib.auth.models import User -import getpass -import os -import sys -import re - -RE_VALID_USERNAME = re.compile('\w+$') - -def createsuperuser(username=None, email=None, password=None): - """ - Helper function for creating a superuser from the command line. All - arguments are optional and will be prompted-for if invalid or not given. - """ - try: - import pwd - except ImportError: - default_username = '' - else: - # Determine the current system user's username, to use as a default. - default_username = pwd.getpwuid(os.getuid())[0].replace(' ', '').lower() - - # 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 = '' - - try: - while 1: - if not username: - input_msg = 'Username' - if default_username: - input_msg += ' (Leave blank to use %r)' % default_username - username = raw_input(input_msg + ': ') - if default_username and username == '': - username = default_username - if not RE_VALID_USERNAME.match(username): - sys.stderr.write("Error: That username is invalid. Use only letters, digits and underscores.\n") - username = None - continue - try: - User.objects.get(username=username) - except User.DoesNotExist: - break - else: - sys.stderr.write("Error: That username is already taken.\n") - username = None - while 1: - if not email: - email = raw_input('E-mail address: ') - try: - validators.isValidEmail(email, None) - except validators.ValidationError: - sys.stderr.write("Error: That e-mail address is invalid.\n") - email = None - else: - break - while 1: - if not password: - password = getpass.getpass() - password2 = getpass.getpass('Password (again): ') - if password != password2: - sys.stderr.write("Error: Your passwords didn't match.\n") - password = None - continue - if password.strip() == '': - sys.stderr.write("Error: Blank passwords aren't allowed.\n") - password = None - continue - break - except KeyboardInterrupt: - sys.stderr.write("\nOperation cancelled.\n") - sys.exit(1) - u = User.objects.create_user(username, email, password) - u.is_staff = True - u.is_active = True - u.is_superuser = True - u.save() - print "Superuser created successfully." - if __name__ == "__main__": - createsuperuser() + from django.core.management import call_command + call_command("createuseruser") diff --git a/django/contrib/auth/management.py b/django/contrib/auth/management/__init__.py similarity index 79% rename from django/contrib/auth/management.py rename to django/contrib/auth/management/__init__.py index 2b4cb8bd19..5f9908be5f 100644 --- a/django/contrib/auth/management.py +++ b/django/contrib/auth/management/__init__.py @@ -32,7 +32,7 @@ def create_permissions(app, created_models, verbosity): def create_superuser(app, created_models, verbosity, **kwargs): from django.contrib.auth.models import User - from django.contrib.auth.create_superuser import createsuperuser as do_create + from django.core.management import call_command if User 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): " @@ -42,8 +42,10 @@ def create_superuser(app, created_models, verbosity, **kwargs): confirm = raw_input('Please enter either "yes" or "no": ') continue if confirm == 'yes': - do_create() + call_command("createsuperuser") break -dispatcher.connect(create_permissions, signal=signals.post_syncdb) -dispatcher.connect(create_superuser, sender=auth_app, signal=signals.post_syncdb) +if 'create_permissions' not in [i.__name__ for i in dispatcher.getAllReceivers(signal=signals.post_syncdb)]: + dispatcher.connect(create_permissions, signal=signals.post_syncdb) +if 'create_superuser' not in [i.__name__ for i in dispatcher.getAllReceivers(signal=signals.post_syncdb, sender=auth_app)]: + dispatcher.connect(create_superuser, sender=auth_app, signal=signals.post_syncdb) \ No newline at end of file diff --git a/django/contrib/auth/management/commands/__init__.py b/django/contrib/auth/management/commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/django/contrib/auth/management/commands/createsuperuser.py b/django/contrib/auth/management/commands/createsuperuser.py new file mode 100644 index 0000000000..ab6bd78d18 --- /dev/null +++ b/django/contrib/auth/management/commands/createsuperuser.py @@ -0,0 +1,122 @@ +""" +Management utility to create superusers. +""" + +import getpass +import os +import re +import sys +from optparse import make_option +from django.contrib.auth.models import User, UNUSABLE_PASSWORD +from django.core import validators +from django.core.management.base import BaseCommand, CommandError + +RE_VALID_USERNAME = re.compile('\w+$') + +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.'), + ) + 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') + + # 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: + validators.isValidEmail(email, None) + except validators.ValidationError: + raise CommandError("Invalid email address.") + password = '' + + # Try to determine the current system user's username to use as a default. + try: + import pwd + except ImportError: + default_username = '' + else: + default_username = pwd.getpwuid(os.getuid())[0].replace(' ', '').lower() + + # 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: + try: + + # Get a username + while 1: + if not username: + input_msg = 'Username' + if default_username: + input_msg += ' (Leave blank to use %r)' % default_username + username = raw_input(input_msg + ': ') + if default_username and username == '': + username = default_username + if not RE_VALID_USERNAME.match(username): + sys.stderr.write("Error: That username is invalid. Use only letters, digits and underscores.\n") + username = None + continue + try: + User.objects.get(username=username) + except User.DoesNotExist: + break + else: + sys.stderr.write("Error: That username is already taken.\n") + username = None + + # Get an email + while 1: + if not email: + email = raw_input('E-mail address: ') + try: + validators.isValidEmail(email, None) + except validators.ValidationError: + sys.stderr.write("Error: That e-mail address is invalid.\n") + email = None + else: + break + + # Get a password + while 1: + if not password: + password = getpass.getpass() + password2 = getpass.getpass('Password (again): ') + if password != password2: + sys.stderr.write("Error: Your passwords didn't match.\n") + password = None + continue + if password.strip() == '': + sys.stderr.write("Error: Blank passwords aren't allowed.\n") + password = None + continue + break + except KeyboardInterrupt: + sys.stderr.write("\nOperation cancelled.\n") + sys.exit(1) + + User.objects.create_superuser(username, email, password) + print "Superuser created successfully." diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index c8670655d4..f74d1d7761 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -116,6 +116,13 @@ class UserManager(models.Manager): user.save() return user + def create_superuser(self, username, email, password): + u = self.create_user(username, email, password) + u.is_staff = True + u.is_active = True + u.is_superuser = True + u.save() + def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'): "Generates a random password with the given length and given allowed_chars" # Note that default value of allowed_chars does not have "I" or letters diff --git a/django/contrib/auth/tests.py b/django/contrib/auth/tests.py index d369ac524c..81094ca85f 100644 --- a/django/contrib/auth/tests.py +++ b/django/contrib/auth/tests.py @@ -35,4 +35,21 @@ False [] >>> a.user_permissions.all() [] + +# +# Tests for createsuperuser management command. +# It's nearly impossible to test the interactive mode -- a command test helper +# would be needed (and *awesome*) -- so just test the non-interactive mode. +# This covers most of the important validation, but not all. +# +>>> from django.core.management import call_command + +>>> call_command("createsuperuser", noinput=True, username="joe", email="joe@somewhere.org") +Superuser created successfully. + +>>> u = User.objects.get(username="joe") +>>> u.email +u'joe@somewhere.org' +>>> u.password +u'!' """ \ No newline at end of file diff --git a/docs/authentication.txt b/docs/authentication.txt index 79eaf673e7..8c5f5b7c3f 100644 --- a/docs/authentication.txt +++ b/docs/authentication.txt @@ -263,14 +263,25 @@ Creating superusers ------------------- ``manage.py syncdb`` prompts you to create a superuser the first time you run -it after adding ``'django.contrib.auth'`` to your ``INSTALLED_APPS``. But if -you need to create a superuser after that via the command line, you can use the -``create_superuser.py`` utility. Just run this command:: +it after adding ``'django.contrib.auth'`` to your ``INSTALLED_APPS``. If you need +to create a superuser at a later date, you can use a command line utility. + +**New in Django development version.**:: + + manage.py createsuperuser --username=joe --email=joe@example.com + +You will be prompted for a password. Once entered, the user is created. If you +leave off the ``--username`` or the ``--email`` option, It will prompt you for +those values as well. + +If you're using an older release of Django, the old way of creating a superuser +on the command line still works:: python /path/to/django/contrib/auth/create_superuser.py -Make sure to substitute ``/path/to/`` with the path to the Django codebase on -your filesystem. +Where ``/path/to`` is the path to the Django codebase on your filesystem. The +``manage.py`` command is prefered since it'll figure out the correct path and +environment for you. Storing additional information about users ------------------------------------------