From b615a8b570bb04fbf1f0d336b3d38e796d435d0e Mon Sep 17 00:00:00 2001 From: Besart Sulejmani Date: Thu, 25 Jan 2024 13:59:28 +0100 Subject: [PATCH 1/2] add create django superuser if django superuser username is set --- bin/docker_start.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/bin/docker_start.sh b/bin/docker_start.sh index 2516c4dd..90f2c706 100755 --- a/bin/docker_start.sh +++ b/bin/docker_start.sh @@ -37,6 +37,18 @@ if [ -d $fixtures_dir ]; then done fi +# Create superuser +# specify password by setting DJANGO_SUPERUSER_PASSWORD in the env +# specify username by setting DJANGO_SUPERUSER_USERNAME in the env +# specify email by setting DJANGO_SUPERUSER_EMAIL in the env +if [ -n "${DJANGO_SUPERUSER_USERNAME}" ]; then + python src/manage.py createsuperuser \ + --no-input \ + --username "${DJANGO_SUPERUSER_USERNAME}" \ + --email "${DJANGO_SUPERUSER_EMAIL:-admin@admin.org}" + unset DJANGO_SUPERUSER_USERNAME DJANGO_SUPERUSER_EMAIL DJANGO_SUPERUSER_PASSWORD +fi + # Start server >&2 echo "Starting server" uwsgi \ From f3f84f4098bf6dfc545a98e77888cf188ef55110 Mon Sep 17 00:00:00 2001 From: Besart Sulejmani Date: Thu, 8 Feb 2024 13:07:13 +0100 Subject: [PATCH 2/2] add createinitialsuperuser command --- bin/docker_start.sh | 16 +- .../accounts/management/__init__.py | 0 .../accounts/management/commands/__init__.py | 0 .../commands/createinitialsuperuser.py | 72 +++++++++ .../tests/test_createinitialsuperuser.py | 146 ++++++++++++++++++ 5 files changed, 226 insertions(+), 8 deletions(-) create mode 100644 src/objecttypes/accounts/management/__init__.py create mode 100644 src/objecttypes/accounts/management/commands/__init__.py create mode 100644 src/objecttypes/accounts/management/commands/createinitialsuperuser.py create mode 100644 src/objecttypes/accounts/tests/test_createinitialsuperuser.py diff --git a/bin/docker_start.sh b/bin/docker_start.sh index 90f2c706..5518b6a6 100755 --- a/bin/docker_start.sh +++ b/bin/docker_start.sh @@ -38,15 +38,15 @@ if [ -d $fixtures_dir ]; then fi # Create superuser -# specify password by setting DJANGO_SUPERUSER_PASSWORD in the env -# specify username by setting DJANGO_SUPERUSER_USERNAME in the env -# specify email by setting DJANGO_SUPERUSER_EMAIL in the env -if [ -n "${DJANGO_SUPERUSER_USERNAME}" ]; then - python src/manage.py createsuperuser \ +# specify password by setting OBJECTTYPE_SUPERUSER_PASSWORD in the env +# specify username by setting OBJECTTYPE_SUPERUSER_USERNAME in the env +# specify email by setting OBJECTTYPE_SUPERUSER_EMAIL in the env +if [ -n "${OBJECTTYPE_SUPERUSER_USERNAME}" ]; then + python src/manage.py createinitialsuperuser \ --no-input \ - --username "${DJANGO_SUPERUSER_USERNAME}" \ - --email "${DJANGO_SUPERUSER_EMAIL:-admin@admin.org}" - unset DJANGO_SUPERUSER_USERNAME DJANGO_SUPERUSER_EMAIL DJANGO_SUPERUSER_PASSWORD + --username "${OBJECTTYPE_SUPERUSER_USERNAME}" \ + --email "${OBJECTTYPE_SUPERUSER_EMAIL:-admin@admin.org}" + unset OBJECTTYPE_SUPERUSER_USERNAME OBJECTTYPE_SUPERUSER_EMAIL OBJECTTYPE_SUPERUSER_PASSWORD fi # Start server diff --git a/src/objecttypes/accounts/management/__init__.py b/src/objecttypes/accounts/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/objecttypes/accounts/management/commands/__init__.py b/src/objecttypes/accounts/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/objecttypes/accounts/management/commands/createinitialsuperuser.py b/src/objecttypes/accounts/management/commands/createinitialsuperuser.py new file mode 100644 index 00000000..6ea17228 --- /dev/null +++ b/src/objecttypes/accounts/management/commands/createinitialsuperuser.py @@ -0,0 +1,72 @@ +import os + +from django.conf import settings +from django.contrib.auth.management.commands.createsuperuser import ( + Command as BaseCommand, +) +from django.core.mail import send_mail +from django.urls import reverse + + +class Command(BaseCommand): + def add_arguments(self, parser): + super().add_arguments(parser) + + parser.add_argument( + "--password", + help="Set the password when the superuser is initially created.", + ) + parser.add_argument( + "--generate-password", + action="store_true", + help=( + "Generate and e-mail the password. The --password option and " + "environment variable overrule this flag." + ), + ) + + def handle(self, **options): + username = options[self.UserModel.USERNAME_FIELD] + database = options["database"] + qs = self.UserModel._default_manager.db_manager(database).filter( + **{self.UserModel.USERNAME_FIELD: username} + ) + if qs.exists(): + self.stdout.write( + self.style.WARNING("Superuser account already exists, exiting") + ) + return + + password = options.get("password") or os.environ.get( + "OBJECTTYPE_SUPERUSER_PASSWORD" + ) + + if password or options["generate_password"]: + options["interactive"] = False + + # perform user creation from core Django + super().handle(**options) + + user = qs.get() + + if not password and options["generate_password"]: + password = self.UserModel.objects.make_random_password(length=20) + + if password: + self.stdout.write("Setting user password...") + user.set_password(password) + user.save() + + if options["generate_password"]: + try: + link = f'{settings.ALLOWED_HOSTS[0]}{reverse("admin:index")}' + except IndexError: + link = "unknown url" + + send_mail( + f"Credentials for {settings.PROJECT_NAME} ({link})", + f"Credentials for project: {settings.PROJECT_NAME}\n\nUsername: {username}\nPassword: {password}", + settings.DEFAULT_FROM_EMAIL, + [user.email], + fail_silently=False, + ) diff --git a/src/objecttypes/accounts/tests/test_createinitialsuperuser.py b/src/objecttypes/accounts/tests/test_createinitialsuperuser.py new file mode 100644 index 00000000..fe208e09 --- /dev/null +++ b/src/objecttypes/accounts/tests/test_createinitialsuperuser.py @@ -0,0 +1,146 @@ +import io +import os + +from django.conf import settings +from django.core import mail +from django.core.management import call_command +from django.test import TestCase, override_settings +from django.urls import exceptions, reverse + +from ..models import User + + +class CreateInitialSuperuserTests(TestCase): + def test_create_initial_superuser_command(self): + call_command( + "createinitialsuperuser", + username="maykin", + email="support@maykinmedia.nl", + generate_password=True, + stdout=io.StringIO(), + stderr=io.StringIO(), + ) + user = User.objects.get() + + self.assertTrue(user.has_usable_password()) + self.assertTrue(user.is_active) + self.assertTrue(user.is_staff) + self.assertTrue(user.is_superuser) + + self.assertEqual(len(mail.outbox), 1) + + sent_mail = mail.outbox[0] + try: + link = f'{settings.ALLOWED_HOSTS[0]}{reverse("admin:index")}' + except exceptions.NoReverseMatch: + link = settings.ALLOWED_HOSTS[0] + self.assertEqual( + sent_mail.subject, f"Credentials for {settings.PROJECT_NAME} ({link})" + ) + self.assertListEqual(sent_mail.recipients(), ["support@maykinmedia.nl"]) + + @override_settings(ALLOWED_HOSTS=[]) + def test_create_initial_superuser_command_allowed_hosts_empty(self): + call_command( + "createinitialsuperuser", + username="maykin", + email="support@maykinmedia.nl", + generate_password=True, + stdout=io.StringIO(), + stderr=io.StringIO(), + ) + user = User.objects.get() + + self.assertTrue(user.has_usable_password()) + self.assertTrue(user.is_active) + self.assertTrue(user.is_staff) + self.assertTrue(user.is_superuser) + + self.assertEqual(len(mail.outbox), 1) + + sent_mail = mail.outbox[0] + link = "unknown url" + self.assertEqual( + sent_mail.subject, f"Credentials for {settings.PROJECT_NAME} ({link})" + ) + self.assertListEqual(sent_mail.recipients(), ["support@maykinmedia.nl"]) + + def test_create_from_cli(self): + call_command( + "createinitialsuperuser", + "--username=admin", + "--password=admin", + "--email=admin@example.com", + "--no-input", + stdout=io.StringIO(), + ) + + user = User.objects.get() + self.assertTrue(user.is_staff) + self.assertTrue(user.is_superuser) + + self.assertEqual(user.username, "admin") + self.assertEqual(user.email, "admin@example.com") + self.assertTrue(user.check_password("admin")) + + def test_command_noop_if_user_exists(self): + User.objects.create(username="admin") + + call_command( + "createinitialsuperuser", + "--username=admin", + "--password=admin", + "--email=admin@example.com", + "--no-input", + stdout=io.StringIO(), + ) + + self.assertEqual(User.objects.count(), 1) + user = User.objects.get() + self.assertFalse(user.is_staff) + self.assertFalse(user.is_superuser) + + self.assertEqual(user.username, "admin") + self.assertEqual(user.email, "") + self.assertFalse(user.check_password("admin")) + + def test_password_from_env(self): + os.environ["OBJECTTYPE_SUPERUSER_PASSWORD"] = "admin" + + def reset_env(): + del os.environ["OBJECTTYPE_SUPERUSER_PASSWORD"] + + self.addCleanup(reset_env) + + call_command( + "createinitialsuperuser", + "--username=admin", + "--email=admin@example.com", + "--no-input", + stdout=io.StringIO(), + ) + + user = User.objects.get() + self.assertTrue(user.is_staff) + self.assertTrue(user.is_superuser) + + self.assertEqual(user.username, "admin") + self.assertEqual(user.email, "admin@example.com") + self.assertTrue(user.check_password("admin")) + + def test_without_password(self): + call_command( + "createinitialsuperuser", + "--username=admin", + "--email=admin@example.com", + "--no-input", + stdout=io.StringIO(), + ) + + user = User.objects.get() + self.assertTrue(user.is_staff) + self.assertTrue(user.is_superuser) + + self.assertEqual(user.username, "admin") + self.assertEqual(user.email, "admin@example.com") + self.assertFalse(user.check_password("admin"))