mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 11:16:47 +00:00
- create_admin had a branch for `"test" in sys.argv` that gave the admin the password "admin"; 17 places signed in with it, and failed silently with ADMIN_PASSWORD set in the environment. The tests now make their own superuser and force_login it, and the shipped code has no test branch: the test runner gives migrate a password (also when ADMIN_PASSWORD is set but empty), so no data/admin_password is written into the checkout. - AccountsTestCase builds its rows in setUpTestData instead of setUpClass and deleting them by hand. - accounts tests set OTP_ENABLED with override_settings instead of assigning settings (one assignment was never undone, so later tests in the process ran with OTP on), and undo their app_settings change. The OTP login class reloads the URLs again only after the setting is off. - computes/tests/test_views.py reached the machine's own libvirt (localhost over the socket) when TEST_LIBVIRT_HOST was unset; its live tests now run only with TEST_LIBVIRT_HOST, like the other live tests, as the README says.
92 lines
4.3 KiB
Python
92 lines
4.3 KiB
Python
"""The generated admin password must not be printed to the log."""
|
|
import io
|
|
import os
|
|
import stat
|
|
import tempfile
|
|
from contextlib import redirect_stdout
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from django.apps import apps
|
|
from django.contrib.auth import authenticate, get_user_model
|
|
from django.test import RequestFactory, TestCase, override_settings
|
|
|
|
from accounts.apps import create_admin
|
|
from accounts.models import UserAttributes
|
|
|
|
|
|
class GeneratedAdminPasswordTestCase(TestCase):
|
|
def _run_create_admin(self, base_dir, plan=None):
|
|
get_user_model().objects.all().delete()
|
|
if plan is None:
|
|
migration = MagicMock(app_label="accounts")
|
|
migration.name = "0001_initial"
|
|
plan = [(migration, False)]
|
|
out = io.StringIO()
|
|
env = {k: v for k, v in os.environ.items() if k not in ("ADMIN_PASSWORD", "ADMIN_USERNAME")}
|
|
with override_settings(BASE_DIR=Path(base_dir)), patch.dict(
|
|
os.environ, env, clear=True
|
|
), redirect_stdout(out):
|
|
create_admin(sender=None, plan=plan, apps=apps)
|
|
return out.getvalue()
|
|
|
|
def test_password_is_written_to_a_private_file_not_printed(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
(Path(tmp) / "data").mkdir()
|
|
output = self._run_create_admin(tmp)
|
|
password_file = Path(tmp) / "data" / "admin_password"
|
|
|
|
self.assertTrue(password_file.exists())
|
|
password = password_file.read_text().strip()
|
|
self.assertEqual(stat.S_IMODE(password_file.stat().st_mode), 0o600)
|
|
self.assertNotIn(password, output)
|
|
self.assertIn(str(password_file), output)
|
|
self.assertIsNotNone(authenticate(RequestFactory().get("/"), username="admin", password=password))
|
|
self.assertTrue(UserAttributes.objects.get(user__username="admin").must_change_password)
|
|
|
|
def test_does_not_follow_a_planted_symlink(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
(Path(tmp) / "data").mkdir()
|
|
victim = Path(tmp) / "victim"
|
|
victim.write_text("original\n")
|
|
(Path(tmp) / "data" / "admin_password").symlink_to(victim)
|
|
|
|
self._run_create_admin(tmp)
|
|
|
|
password_file = Path(tmp) / "data" / "admin_password"
|
|
self.assertEqual(victim.read_text(), "original\n")
|
|
self.assertFalse(password_file.is_symlink())
|
|
self.assertEqual(stat.S_IMODE(password_file.stat().st_mode), 0o600)
|
|
|
|
def test_storage_failure_does_not_print_password_or_create_an_admin(self):
|
|
password = "review-dummy-generated-password"
|
|
output = io.StringIO()
|
|
with tempfile.TemporaryDirectory() as tmp, patch(
|
|
"secrets.token_urlsafe", return_value=password
|
|
), patch("accounts.apps.os.open", side_effect=PermissionError("read-only")), redirect_stdout(output):
|
|
with self.assertRaisesRegex(RuntimeError, "Cannot securely store"):
|
|
self._run_create_admin(tmp)
|
|
self.assertNotIn(password, output.getvalue())
|
|
self.assertFalse(get_user_model().objects.exists())
|
|
|
|
def test_rerunning_migrate_after_a_storage_failure_creates_the_admin(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
with patch("accounts.apps.os.open", side_effect=PermissionError("read-only")), redirect_stdout(io.StringIO()):
|
|
with self.assertRaises(RuntimeError):
|
|
self._run_create_admin(tmp)
|
|
# The second migrate has nothing left to apply: an empty plan.
|
|
(Path(tmp) / "data").mkdir(exist_ok=True)
|
|
self._run_create_admin(tmp, plan=[])
|
|
self.assertTrue(get_user_model().objects.filter(username="admin").exists())
|
|
|
|
def test_flush_does_not_create_an_admin(self):
|
|
get_user_model().objects.all().delete()
|
|
create_admin(sender=None, plan=None, apps=apps)
|
|
self.assertFalse(get_user_model().objects.exists())
|
|
|
|
def test_migrating_another_app_before_auth_creates_nothing(self):
|
|
get_user_model().objects.all().delete()
|
|
state_without_auth = MagicMock()
|
|
state_without_auth.get_model.side_effect = LookupError
|
|
create_admin(sender=None, plan=[], apps=state_without_auth)
|
|
self.assertFalse(get_user_model().objects.exists())
|