summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHasan Ramezani <hasan.r67@gmail.com>2019-06-21 23:38:27 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2019-07-02 12:55:09 +0200
commita5308514fb4bc5086c9a16a8a24a945eeebb073c (patch)
tree0f0eb8295a6b3cecd605acd5e20337ccabffbce9
parent4b32d039dbb59b3c3e76587df5c58150e752d9ac (diff)
Fixed #27801 -- Made createsuperuser fall back to environment variables for password and required fields.
-rw-r--r--django/contrib/auth/management/commands/createsuperuser.py17
-rw-r--r--docs/ref/django-admin.txt20
-rw-r--r--docs/releases/3.0.txt4
-rw-r--r--tests/auth_tests/test_management.py56
4 files changed, 90 insertions, 7 deletions
diff --git a/django/contrib/auth/management/commands/createsuperuser.py b/django/contrib/auth/management/commands/createsuperuser.py
index d700ce4c0e..ece66dfad1 100644
--- a/django/contrib/auth/management/commands/createsuperuser.py
+++ b/django/contrib/auth/management/commands/createsuperuser.py
@@ -2,6 +2,7 @@
Management utility to create superusers.
"""
import getpass
+import os
import sys
from django.contrib.auth import get_user_model
@@ -138,6 +139,13 @@ class Command(BaseCommand):
user_data[PASSWORD_FIELD] = password
else:
# Non-interactive mode.
+ # Use password from environment variable, if provided.
+ if PASSWORD_FIELD in user_data and 'DJANGO_SUPERUSER_PASSWORD' in os.environ:
+ user_data[PASSWORD_FIELD] = os.environ['DJANGO_SUPERUSER_PASSWORD']
+ # Use username from environment variable, if not provided in
+ # options.
+ if username is None:
+ username = os.environ.get('DJANGO_SUPERUSER_' + self.UserModel.USERNAME_FIELD.upper())
if username is None:
raise CommandError('You must use --%s with --noinput.' % self.UserModel.USERNAME_FIELD)
else:
@@ -147,11 +155,12 @@ class Command(BaseCommand):
user_data[self.UserModel.USERNAME_FIELD] = username
for field_name in self.UserModel.REQUIRED_FIELDS:
- if options[field_name]:
- field = self.UserModel._meta.get_field(field_name)
- user_data[field_name] = field.clean(options[field_name], None)
- else:
+ env_var = 'DJANGO_SUPERUSER_' + field_name.upper()
+ value = options[field_name] or os.environ.get(env_var)
+ if not value:
raise CommandError('You must use --%s with --noinput.' % field_name)
+ field = self.UserModel._meta.get_field(field_name)
+ user_data[field_name] = field.clean(value, None)
self.UserModel._default_manager.db_manager(database).create_superuser(**user_data)
if options['verbosity'] >= 1:
diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt
index 69e00b0428..e139c8128f 100644
--- a/docs/ref/django-admin.txt
+++ b/docs/ref/django-admin.txt
@@ -1560,9 +1560,23 @@ useful if you need to create an initial superuser account or if you need to
programmatically generate superuser accounts for your site(s).
When run interactively, this command will prompt for a password for
-the new superuser account. When run non-interactively, no password
-will be set, and the superuser account will not be able to log in until
-a password has been manually set for it.
+the new superuser account. When run non-interactively, you can provide
+a password by setting the ``DJANGO_SUPERUSER_PASSWORD`` environment variable.
+Otherwise, no password will be set, and the superuser account will not be able
+to log in until a password has been manually set for it.
+
+In non-interactive mode, the
+:attr:`~django.contrib.auth.models.CustomUser.USERNAME_FIELD` and required
+fields (listed in
+:attr:`~django.contrib.auth.models.CustomUser.REQUIRED_FIELDS`) fall back to
+``DJANGO_SUPERUSER_<uppercase_field_name>`` environment variables, unless they
+are overridden by a command line argument. For example, to provide an ``email``
+field, you can use ``DJANGO_SUPERUSER_EMAIL`` environment variable.
+
+.. versionchanged:: 3.0
+
+ Support for using ``DJANGO_SUPERUSER_PASSWORD`` and
+ ``DJANGO_SUPERUSER_<uppercase_field_name>`` environment variables was added.
.. django-admin-option:: --username USERNAME
.. django-admin-option:: --email EMAIL
diff --git a/docs/releases/3.0.txt b/docs/releases/3.0.txt
index 4c33e7c309..c7f1a4e78b 100644
--- a/docs/releases/3.0.txt
+++ b/docs/releases/3.0.txt
@@ -102,6 +102,10 @@ Minor features
password fields in :mod:`django.contrib.auth.forms` for better interaction
with browser password managers.
+* :djadmin:`createsuperuser` now falls back to environment variables for
+ password and required fields, when a corresponding command line argument
+ isn't provided in non-interactive mode.
+
:mod:`django.contrib.contenttypes`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/auth_tests/test_management.py b/tests/auth_tests/test_management.py
index d0a91f3261..bf9b063102 100644
--- a/tests/auth_tests/test_management.py
+++ b/tests/auth_tests/test_management.py
@@ -1,5 +1,6 @@
import builtins
import getpass
+import os
import sys
from datetime import date
from io import StringIO
@@ -905,6 +906,61 @@ class CreatesuperuserManagementCommandTestCase(TestCase):
test(self)
+ @mock.patch.dict(os.environ, {
+ 'DJANGO_SUPERUSER_PASSWORD': 'test_password',
+ 'DJANGO_SUPERUSER_USERNAME': 'test_superuser',
+ 'DJANGO_SUPERUSER_EMAIL': 'joe@somewhere.org',
+ 'DJANGO_SUPERUSER_FIRST_NAME': 'ignored_first_name',
+ })
+ def test_environment_variable_non_interactive(self):
+ call_command('createsuperuser', interactive=False, stdout=StringIO())
+ user = User.objects.get(username='test_superuser')
+ self.assertEqual(user.email, 'joe@somewhere.org')
+ self.assertTrue(user.check_password('test_password'))
+ # Environment variables are ignored for non-required fields.
+ self.assertEqual(user.first_name, '')
+
+ @mock.patch.dict(os.environ, {
+ 'DJANGO_SUPERUSER_USERNAME': 'test_superuser',
+ 'DJANGO_SUPERUSER_EMAIL': 'joe@somewhere.org',
+ })
+ def test_ignore_environment_variable_non_interactive(self):
+ # Environment variables are ignored in non-interactive mode, if
+ # provided by a command line arguments.
+ call_command(
+ 'createsuperuser',
+ interactive=False,
+ username='cmd_superuser',
+ email='cmd@somewhere.org',
+ stdout=StringIO(),
+ )
+ user = User.objects.get(username='cmd_superuser')
+ self.assertEqual(user.email, 'cmd@somewhere.org')
+ self.assertFalse(user.has_usable_password())
+
+ @mock.patch.dict(os.environ, {
+ 'DJANGO_SUPERUSER_PASSWORD': 'test_password',
+ 'DJANGO_SUPERUSER_USERNAME': 'test_superuser',
+ 'DJANGO_SUPERUSER_EMAIL': 'joe@somewhere.org',
+ })
+ def test_ignore_environment_variable_interactive(self):
+ # Environment variables are ignored in interactive mode.
+ @mock_inputs({'password': 'cmd_password'})
+ def test(self):
+ call_command(
+ 'createsuperuser',
+ interactive=True,
+ username='cmd_superuser',
+ email='cmd@somewhere.org',
+ stdin=MockTTY(),
+ stdout=StringIO(),
+ )
+ user = User.objects.get(username='cmd_superuser')
+ self.assertEqual(user.email, 'cmd@somewhere.org')
+ self.assertTrue(user.check_password('cmd_password'))
+
+ test(self)
+
class MultiDBCreatesuperuserTestCase(TestCase):
databases = {'default', 'other'}