From 6967e11ed135edb7817f13a63edb918c8b8774ae Mon Sep 17 00:00:00 2001 From: Lucas Cabello Date: Tue, 26 May 2026 16:44:28 -0400 Subject: [PATCH] Fix reset-admin-password crashing when admin exists without superuser flag The command used get_or_create with username, is_superuser, and is_staff as lookup criteria. When an admin user existed with is_superuser=False, the lookup found nothing and the subsequent INSERT hit a UniqueViolation on the username constraint. Switch to update_or_create so the lookup uses username only, and is_superuser/is_staff are set via defaults on both create and update paths. Fixes: AAP-76361 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../commands/reset-admin-password.py | 5 +- .../unit/test_reset_admin_password_command.py | 54 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 pulpcore/tests/unit/test_reset_admin_password_command.py diff --git a/pulpcore/app/management/commands/reset-admin-password.py b/pulpcore/app/management/commands/reset-admin-password.py index 6393259c6eb..590ebd644c7 100644 --- a/pulpcore/app/management/commands/reset-admin-password.py +++ b/pulpcore/app/management/commands/reset-admin-password.py @@ -34,7 +34,10 @@ def add_arguments(self, parser): ) def handle(self, *args, **options): - user = User.objects.get_or_create(username="admin", is_superuser=True, is_staff=True)[0] + user, _ = User.objects.update_or_create( + username="admin", + defaults={"is_superuser": True, "is_staff": True}, + ) if options["random"]: alphabet = string.ascii_letters + string.digits password = "".join(secrets.choice(alphabet) for i in range(20)) diff --git a/pulpcore/tests/unit/test_reset_admin_password_command.py b/pulpcore/tests/unit/test_reset_admin_password_command.py new file mode 100644 index 00000000000..e4fd6cd928f --- /dev/null +++ b/pulpcore/tests/unit/test_reset_admin_password_command.py @@ -0,0 +1,54 @@ +from io import StringIO + +import pytest +from django.contrib.auth import get_user_model +from django.core.management import call_command + +User = get_user_model() + + +@pytest.mark.django_db +def test_creates_new_admin_with_password(): + out = StringIO() + call_command("reset-admin-password", password="testpass123", stdout=out) + user = User.objects.get(username="admin") + assert user.is_superuser is True + assert user.is_staff is True + assert user.check_password("testpass123") + assert "Successfully set password" in out.getvalue() + + +@pytest.mark.django_db +def test_resets_password_on_existing_superuser(): + User.objects.create_user(username="admin", password="old", is_superuser=True, is_staff=True) + out = StringIO() + call_command("reset-admin-password", password="newpass", stdout=out) + user = User.objects.get(username="admin") + assert user.is_superuser is True + assert user.is_staff is True + assert user.check_password("newpass") + + +@pytest.mark.django_db +def test_promotes_existing_non_superuser_admin(): + User.objects.create_user(username="admin", password="old", is_superuser=False, is_staff=False) + out = StringIO() + call_command("reset-admin-password", password="newpass", stdout=out) + user = User.objects.get(username="admin") + assert user.is_superuser is True + assert user.is_staff is True + assert user.check_password("newpass") + + +@pytest.mark.django_db +def test_random_password(): + out = StringIO() + call_command("reset-admin-password", random=True, stdout=out) + user = User.objects.get(username="admin") + assert user.is_superuser is True + assert user.is_staff is True + output = out.getvalue() + assert "Successfully set" in output + password = output.split('"')[-2] + assert len(password) == 20 + assert user.check_password(password)